home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / manual-p / vh-man2h.000 / vh-man2h / vh-man2html-1.3 / man2html.c < prev    next >
C/C++ Source or Header  |  1996-05-03  |  91KB  |  3,667 lines

  1. /*
  2. ** This program was written by Richard Verhoeven (NL:5482ZX35)
  3. ** at the Eindhoven University of Technology. Email: rcb5@win.tue.nl
  4. **
  5. ** Permission is granted to distribute, modify and use this program as long
  6. ** as this comment is not removed or changed.
  7. */
  8.  
  9. /* 
  10.  * man2html-linux-1.0/1.1
  11.  * This version modified for Redhat/Caldera linux - March 1996.
  12.  * Michael Hamilton <michael@actrix.gen.nz>.
  13.  *
  14.  * man2html-linux-1.2
  15.  * Added support for BSD mandoc pages - I didn't have any documentation
  16.  * on the mandoc macros, so I may have missed some.
  17.  * Michael Hamilton <michael@actrix.gen.nz>.
  18.  *
  19.  * vh-man2html-1.3
  20.  * Renamed to avoid confusion (V for Verhoeven, H for Hamilton).
  21.  */
  22.  
  23. /*
  24. ** If you want to use this program for your WWW server, adjust the line
  25. ** which defines the CGIBASE or compile it with the -DCGIBASE='"..."' option.
  26. **
  27. ** You have to adjust the built-in manpath to your local system. Note that
  28. ** every directory should start and end with the '/' and that the first
  29. ** directory should be "/" to allow a full path as an argument.
  30. **
  31. ** The program first check if PATH_INFO contains some information.
  32. ** If it does (t.i. man2html/some/thing is used), the program will look
  33. ** for a manpage called PATH_INFO in the manpath.
  34. **
  35. ** Otherwise the manpath is searched for the specified command line argument,
  36. ** where the following options can be used:
  37. **
  38. ** name      name of manpage (csh, printf, xv, troff)
  39. ** section   the section (1 2 3 4 5 6 7 8 9 n l 1v ...)
  40. ** -M path   an extra directory to look for manpages (replaces "/")
  41. **
  42. ** If man2html finds multiple manpages that satisfy the options, an index
  43. ** is displayed and the user can make a choice. If only one page is
  44. ** found, that page will be displayed.
  45. **
  46. ** man2html will add links to the converted manpages. The function add_links
  47. ** is used for that. At the moment it will add links as follows, where   
  48. **     indicates what should match to start with:
  49. ** ^^^
  50. ** Recognition           Item            Link
  51. ** ----------------------------------------------------------
  52. ** name(*)               Manpage         ../man?/name.*
  53. **     ^
  54. ** name@hostname         Email address   mailto:name@hostname
  55. **     ^
  56. ** method://string       URL             method://string
  57. **       ^^^
  58. ** www.host.name         WWW server      http://www.host.name
  59. ** ^^^^
  60. ** ftp.host.name         FTP server      ftp://ftp.host.name
  61. ** ^^^^
  62. ** <file.h>              Include file    file:/usr/include/file.h
  63. **      ^^^
  64. **
  65. ** Since man2html does not check if manpages, hosts or email addresses exist,
  66. ** some links might not work. For manpages, some extra checks are performed
  67. ** to make sure not every () pair creates a link. Also out of date pages
  68. ** might point to incorrect places.
  69. **
  70. ** The program will not allow users to get system specific files, such as
  71. ** /etc/passwd. It will check that "man" is part of the specified file and
  72. ** that  "/../" isn't. Even if someone manages to get such file, man2html will
  73. ** handle it like a manpage and will usually not produce any output (or crash).
  74. **
  75. ** If you find any bugs when normal manpages are converted, please report
  76. ** them to me (rcb5@win.tue.nl) after you have checked that man(1) can handle
  77. ** the manpage correct.
  78. **
  79. ** Known bugs and missing features:
  80. **
  81. **  * Equations are not converted at all.
  82. **  * Tables are converted but some features are not possible in html.
  83. **  * The tabbing environment is converted by counting characters and adding
  84. **    spaces. This might go wrong (outside <PRE>)
  85. **  * Some pages look beter if man2html works in troff mode, especially pages
  86. **    with tables. You can deside at compile time which made you want to use.
  87. **
  88. **    -DNROFF=0     troff mode
  89. **    -DNROFF=1     nroff mode   (default)
  90. **
  91. **    if you install both modes, you should compile with the correct CGIBASE.
  92. **  * Some manpages rely on the fact that troff/nroff is used to convert
  93. **    them and use features which are not descripted in the man manpages.
  94. **    (definitions, calculations, conditionals, requests). I can't guarantee
  95. **    that all these features work on all manpages. (I didn't have the
  96. **    time to look through all the available manpages.)
  97. */
  98.  
  99.  
  100. #include <stdio.h>
  101. #include <stdlib.h>
  102. #include <string.h>
  103. #include <sys/stat.h>
  104. #include <dirent.h>
  105. #include <ctype.h>
  106. #include <sys/types.h>
  107. #include <time.h>
  108. #include <sys/time.h>
  109.  
  110. #ifndef TOPLEVELDOC
  111. #define TOPLEVELDOC "/home/httpd/html/man.html"
  112. #endif
  113.  
  114. #ifndef CGIBASE
  115. #define CGIBASE "http://localhost/cgi-bin/man2html"
  116. #endif
  117.  
  118. #ifndef NROFF
  119. #define NROFF 1
  120. #endif
  121.  
  122. static char location_base[200] = "";
  123.  
  124. char *signature = "<HR>\n"
  125. "This document was created by\n"
  126. "<A HREF=\""CGIBASE"\">man2html</A>,\n"
  127. "using the manual pages.<BR>\n"
  128. "Time: %s\n";
  129.  
  130. /* timeformat for signature */
  131. #define TIMEFORMAT "%T GMT, %B %d, %Y"
  132.  
  133. /* BSD mandoc Bl/El lists to HTML list types */
  134. #define BL_DESC_LIST   1
  135. #define BL_BULLET_LIST 2
  136. #define BL_ENUM_LIST   4
  137.  
  138. /* BSD mandoc Bd/Ed example(?) blocks */
  139. #define BD_LITERAL  1
  140. #define BD_INDENT   2
  141.  
  142. /* Don't bother with free(), it will just slow us down, the ultimate
  143.    free will be exit() anyway.  Hack to fix a malloc bug that I couldn't
  144.    track down (michael@actrix.gen.nz).
  145.    */
  146. #define free(x)
  147.  
  148. char *manpath[] = { "/",
  149.             "/usr/X11/man/",
  150.             "/usr/man/",
  151.             "/usr/local/man/",
  152.             "/usr/exp/man/",
  153.             "/usr/openwin/man/",
  154.             "/usr/tex/man/",
  155.             "/usr/www/man/",
  156.             "/usr/lang/man/",
  157.             "/usr/gnu/man/",
  158.             "/usr/motif/man/",
  159.             "/usr/titools/man/",
  160.             "/usr/sunpc/man/",
  161.             "/usr/ncd/man/",
  162.             "/usr/newsprint/man/",
  163.             NULL };
  164.  
  165. char *sections = "123456789nl";
  166.  
  167.  
  168. void usage(void)
  169. {
  170.     char buffer[500];
  171.     FILE *toplevel = fopen(TOPLEVELDOC, "r");
  172.     
  173.     if (!toplevel && errno == ENOENT) {
  174.       printf("Content-type: text/html\n\n"
  175.          "<HTML><HEAD>\n"
  176.          "<TITLE>Manual Pages</TITLE>\n"
  177.          "</HEAD><BODY>\n"
  178.          "<H1>Manual Pages</H1>\n"
  179.          "This is a HyperText interface to the UNIX manpages.\n"
  180.          "You can enter a program name, the section, an extra\n"
  181.          "directory (using -M) or a full name. For example\n"
  182.          "<UL><LI><TT>elm</TT>\n"
  183.          "<LI><TT>elm 1</TT>\n"
  184.          "<LI><TT>-M /usr/local/man elm</TT>\n"
  185.          "<LI><TT>/local/gcc/man/man1/gperf.1</TT>\n"
  186.          "</UL>\n"
  187.          "<ISINDEX>\n"
  188.          "<P>\n"
  189.          "This man2html converter was written by \n"
  190.          "<A HREF=\"http://wsinwp01.win.tue.nl:1234/index.html\">"
  191.          "Richard Verhoeven</A>\n"
  192.          "</BODY></HTML>\n");
  193.       exit(0);      
  194.     }
  195.  
  196.     if (!toplevel) {
  197.       fprintf(stderr, "Error openning %s: %s\n", TOPLEVELDOC, strerror(errno));
  198.       exit(EXIT_FAILURE);
  199.     }
  200.     while (fgets(buffer, 500, toplevel)) {
  201.         fputs(buffer,stdout);
  202.     }
  203.     if (!feof(toplevel)) {
  204.         fprintf(stderr, "Error reading %s\n", TOPLEVELDOC, strerror(errno));
  205.     exit(EXIT_FAILURE);
  206.     }
  207.     fclose(toplevel);
  208.     exit(0);
  209. }
  210.  
  211. /* below this you should not change anything unless you know a lot
  212. ** about this program or about troff.
  213. */
  214.  
  215.  
  216. typedef struct STRDEF STRDEF;
  217. struct STRDEF {
  218.     int nr,slen;
  219.     char *st;
  220.     STRDEF *next;
  221. };
  222.  
  223. typedef struct INTDEF INTDEF;
  224. struct INTDEF {
  225.     int nr;
  226.     int val;
  227.     int incr;
  228.     INTDEF *next;
  229. };
  230.  
  231. static char NEWLINE[2]="\n";
  232. static char idxlabel[6] = "ixAAA";
  233.  
  234. #define INDEXFILE "/tmp/manindex.list"
  235.  
  236. char *fname;
  237. FILE *idxfile;
  238.  
  239. STRDEF *chardef, *strdef, *defdef;
  240. INTDEF *intdef;
  241.  
  242. #define V(A,B) ((A)*256+(B))
  243.  
  244. INTDEF standardint[] = {
  245.     { V('n',' '), NROFF,0, NULL },
  246.     { V('t',' '), 1-NROFF,0, NULL },
  247.     { V('o',' '), 1,0, NULL },
  248.     { V('e',' '), 0,0, NULL },
  249.     { V('.','l'), 70,0,NULL },
  250.     { V('.','$'), 0,0, NULL },
  251.     { V('.','A'), NROFF,0, NULL },
  252.     { V('.','T'), 1-NROFF,0, NULL },
  253.     { V('.','V'), 1,0, NULL }, /* the me package tests for this */
  254.     { 0, 0, 0, NULL } };
  255.  
  256. STRDEF standardstring[] = {
  257.     { V('R',' '), 1, "®", NULL },
  258.     { V('l','q'), 2, "``", NULL },
  259.     { V('r','q'), 2, "''", NULL },
  260.     { 0, 0, NULL, NULL}
  261. };
  262.  
  263.  
  264. STRDEF standardchar[] = {
  265.     { V('*','*'), 1, "*", NULL  },
  266.     { V('*','A'), 1, "A", NULL  },
  267.     { V('*','B'), 1, "B", NULL  },
  268.     { V('*','C'), 2, "Xi", NULL  },
  269.     { V('*','D'), 5, "Delta", NULL  },
  270.     { V('*','E'), 1, "E", NULL  },
  271.     { V('*','F'), 3, "Phi", NULL  },
  272.     { V('*','G'), 5, "Gamma", NULL  },
  273.     { V('*','H'), 5, "Theta", NULL  },
  274.     { V('*','I'), 1, "I", NULL  },
  275.     { V('*','K'), 1, "K", NULL  },
  276.     { V('*','L'), 6, "Lambda", NULL  },
  277.     { V('*','M'), 1, "M", NULL  },
  278.     { V('*','N'), 1, "N", NULL  },
  279.     { V('*','O'), 1, "O", NULL  },
  280.     { V('*','P'), 2, "Pi", NULL  },
  281.     { V('*','Q'), 3, "Psi", NULL  },
  282.     { V('*','R'), 1, "P", NULL  },
  283.     { V('*','S'), 5, "Sigma", NULL  },
  284.     { V('*','T'), 1, "T", NULL  },
  285.     { V('*','U'), 1, "Y", NULL  },
  286.     { V('*','W'), 5, "Omega", NULL  },
  287.     { V('*','X'), 1, "X", NULL  },
  288.     { V('*','Y'), 1, "H", NULL  },
  289.     { V('*','Z'), 1, "Z", NULL  },
  290.     { V('*','a'), 5, "alpha", NULL },
  291.     { V('*','b'), 4, "beta", NULL },
  292.     { V('*','c'), 2, "xi", NULL },
  293.     { V('*','d'), 5, "delta", NULL },
  294.     { V('*','e'), 7, "epsilon", NULL },
  295.     { V('*','f'), 3, "phi", NULL },
  296.     { V('*','g'), 5, "gamma", NULL },
  297.     { V('*','h'), 5, "theta", NULL },
  298.     { V('*','i'), 4, "iota", NULL },
  299.     { V('*','k'), 5, "kappa", NULL },
  300.     { V('*','l'), 6, "lambda", NULL },
  301.     { V('*','m'), 1, "µ", NULL  },
  302.     { V('*','n'), 2, "nu", NULL },
  303.     { V('*','o'), 1, "o", NULL },
  304.     { V('*','p'), 2, "pi", NULL },
  305.     { V('*','q'), 3, "psi", NULL },
  306.     { V('*','r'), 3, "rho", NULL },
  307.     { V('*','s'), 5, "sigma", NULL },
  308.     { V('*','t'), 3, "tau", NULL },
  309.     { V('*','u'), 7, "upsilon", NULL },
  310.     { V('*','w'), 5, "omega", NULL },
  311.     { V('*','x'), 3, "chi", NULL },
  312.     { V('*','y'), 3, "eta", NULL },
  313.     { V('*','z'), 4, "zeta", NULL },
  314.     { V('t','s'), 5, "sigma", NULL },
  315.     { V('+','-'), 1, "±", NULL  },
  316.     { V('1','2'), 1, "½", NULL  },
  317.     { V('1','4'), 1, "¼", NULL  },
  318.     { V('3','4'), 1, "¾", NULL  },
  319.     { V('F','i'), 3, "ffi", NULL  },
  320.     { V('F','l'), 3, "ffl", NULL  },
  321.     { V('a','a'), 1, "´", NULL  },
  322.     { V('a','p'), 1, "~", NULL  },
  323.     { V('b','r'), 1, "|", NULL  },
  324.     { V('b','u'), 1, "*", NULL  },
  325.     { V('b','v'), 1, "|", NULL  },
  326.     { V('c','i'), 1, "o", NULL  },
  327.     { V('c','o'), 1, "©", NULL  },
  328.     { V('c','t'), 1, "¢", NULL  },
  329.     { V('d','e'), 1, "°", NULL  },
  330.     { V('d','g'), 1, "+", NULL  },
  331.     { V('d','i'), 1, "÷", NULL  },
  332.     { V('e','m'), 1, "-", NULL  },
  333.     { V('e','m'), 3, "---", NULL },
  334.     { V('e','q'), 1, "=", NULL  },
  335.     { V('e','s'), 1, "Ø", NULL  },
  336.     { V('f','f'), 2, "ff", NULL  },
  337.     { V('f','i'), 2, "fi", NULL  },
  338.     { V('f','l'), 2, "fl", NULL  },
  339.     { V('f','m'), 1, "´", NULL  },
  340.     { V('g','a'), 1, "`", NULL  },
  341.     { V('h','y'), 1, "-", NULL  },
  342.     { V('l','c'), 2, "|¯", NULL  },
  343.     { V('l','f'), 2, "|_", NULL  },
  344.     { V('l','k'), 1, "<FONT SIZE=+2>{</FONT>", NULL  },
  345.     { V('m','i'), 1, "-", NULL  },
  346.     { V('m','u'), 1, "×", NULL  },
  347.     { V('n','o'), 1, "¬", NULL  },
  348.     { V('o','r'), 1, "|", NULL  },
  349.     { V('p','l'), 1, "+", NULL  },
  350.     { V('r','c'), 2, "¯|", NULL  },
  351.     { V('r','f'), 2, "_|", NULL  },
  352.     { V('r','g'), 1, "®", NULL  },
  353.     { V('r','k'), 1, "<FONT SIZE=+2>}</FONT>", NULL  },
  354.     { V('r','n'), 1, "¯", NULL  },
  355.     { V('r','u'), 1, "_", NULL  },
  356.     { V('s','c'), 1, "§", NULL  },
  357.     { V('s','l'), 1, "/", NULL  },
  358.     { V('s','q'), 2, "[]", NULL  },
  359.     { V('u','l'), 1, "_", NULL  },
  360.     { 0, 0, NULL, NULL  }
  361. };
  362.  
  363. /* default: print code */
  364.  
  365.  
  366. char eqndelimopen=0, eqndelimclose=0;
  367. char escapesym='\\', nobreaksym='\'', controlsym='.', fieldsym=0, padsym=0;
  368.  
  369. char *buffer=NULL;
  370. int buffpos=0, buffmax=0;
  371. int scaninbuff=0;
  372. int itemdepth=0;
  373. int dl_set[20]= { 0 };
  374. int list_options[20][50];
  375. int still_dd=0;
  376. int tabstops[20] = { 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96 };
  377. int maxtstop=12;
  378. int curpos=0;
  379.  
  380. extern char *scan_troff(char *c, int san, char **result);
  381. extern char *scan_troff_mandoc(char *c, int san, char **result);
  382.  
  383. static char **argument=NULL;
  384.  
  385. static char charb[3];
  386.  
  387. void print_sig(void)
  388. {
  389.     char datbuf[500];
  390.     struct tm *timetm;
  391.     time_t clock;
  392.     datbuf[0]='\0';
  393. #ifdef TIMEFORMAT
  394.     clock=time(NULL);
  395.     timetm=gmtime(&clock);
  396.     strftime(datbuf,500,TIMEFORMAT, timetm);
  397. #endif
  398.     printf(signature, datbuf);
  399. }
  400.  
  401. char *expand_char(int nr)
  402. {
  403.   STRDEF *h;
  404.   h=chardef;
  405.   if (!nr) return NULL;
  406.   while (h)
  407.       if (h->nr==nr) {
  408.       curpos+=h->slen;
  409.       return h->st;
  410.       } else
  411.       h=h->next;
  412.   charb[0]=nr/256;
  413.   charb[1]=nr%256;
  414.   charb[2]='\0';
  415.   curpos+=2;
  416.   return charb;
  417. }
  418.  
  419. char *expand_string(int nr)
  420. {
  421.   STRDEF *h=strdef;
  422.   if (!nr) return NULL;
  423.   while (h)
  424.       if (h->nr==nr) {
  425.       curpos+=h->slen;
  426.       return h->st;
  427.     } else
  428.     h=h->next;
  429.   return NULL;
  430. }
  431.  
  432.  
  433. char outbuffer[1024];
  434. int obp=0;
  435. int no_newline_output=0;
  436. int newline_for_fun=0;
  437. int output_possible=0;
  438. int out_length=0;
  439.  
  440. void add_links(char *c)
  441. {
  442.     /*
  443.     ** Add the links to the output.
  444.     ** At the moment the following are recognized:
  445.     **
  446.     ** name(*)                 -> ../man?/name.*
  447.     ** method://string         -> method://string
  448.     ** www.host.name           -> http://www.host.name
  449.     ** ftp.host.name           -> ftp://ftp.host.name
  450.     ** name@host               -> mailto:name@host
  451.     ** <name.h>                -> file:/usr/include/name.h   (guess)
  452.     **
  453.     ** Other possible links to add in the future:
  454.     **
  455.     ** /dir/dir/file  -> file:/dir/dir/file
  456.     */
  457.     int i,j,nr;
  458.     char *f, *g,*h;
  459.     char *idtest[6]; /* url, mailto, www, ftp, manpage */
  460.     out_length+=strlen(c);
  461.     /* search for (section) */
  462.     nr=0;
  463.     idtest[0]=strstr(c+1,"://");
  464.     idtest[1]=strchr(c+1,'@');
  465.     idtest[2]=strstr(c,"www.");
  466.     idtest[3]=strstr(c,"ftp.");
  467.     idtest[4]=strchr(c+1,'(');
  468.     idtest[5]=strstr(c+1,".h>");
  469.     for (i=0; i<6; i++) nr += (idtest[i]!=NULL);
  470.     while (nr) {
  471.     j=-1;
  472.     for (i=0; i<6; i++)
  473.         if (idtest[i] && (j<0 || idtest[i]<idtest[j])) j=i;
  474.     switch (j) {
  475.     case 5: /* <name.h> */
  476.         f=idtest[5];
  477.         h=f+2;
  478.         g=f;
  479.         while (g>c && g[-1]!=';') g--;
  480.         if (g!=c) {
  481.         char t;
  482.         t=*g;
  483.         *g='\0';
  484.         printf("%s",c);
  485.         *g=t;*h='\0';
  486.         printf("<A HREF=\"file:/usr/include/%s\">%s</A>>", g,g);
  487.         c=f+6;
  488.         } else {
  489.         f[5]='\0';
  490.         printf("%s",c);
  491.         f[5]=';';
  492.         c=f+5;
  493.         }
  494.         break;
  495.     case 4: /* manpage */
  496.         f=idtest[j];
  497.         /* check section */
  498.         g=strchr(f,')');
  499.         if (g && f-g<6 && (isalnum(f[-1]) || f[-1]=='>') &&
  500.         ((isdigit(f[1]) && f[1]!='0' &&
  501.           (f[2]==')' || (isalpha(f[2]) && f[3]==')') || f[2]=='X')) ||
  502.          (f[2]==')' && (f[1]=='n' || f[1]=='l')))) {
  503.         /* this might be a link */
  504.         h=f-1;
  505.         /* skip html makeup */
  506.         while (h>c && *h=='>') {
  507.             while (h!=c && *h!='<') h--;
  508.             if (h!=c) h--;
  509.         }
  510.         if (isalnum(*h)) {
  511.             char t,sec,subsec, *e;
  512.             e=h+1;
  513.             sec=f[1];
  514.             subsec=f[2];
  515.             if ((subsec=='X' && f[3]!=')')|| subsec==')') subsec='\0';
  516.             while (h>c && (isalnum(h[-1]) || h[-1]=='_' ||
  517.                     h[-1]=='-' || h[-1]=='.'))
  518.             h--;
  519.             t=*h;
  520.             *h='\0';
  521.             printf("%s", c);
  522.             *h=t;
  523.             t=*e;
  524.             *e='\0';
  525.             if (subsec)
  526.             printf("<A HREF=\""
  527.                    CGIBASE
  528.                    "?man%c/%s.%c%c\">%s</A>",
  529.                    sec, h, sec, tolower(subsec), h);
  530.             else
  531.             printf("<A HREF=\""
  532.                    CGIBASE
  533.                    "?man%c/%s.%c\">%s</A>",
  534.                    sec, h, sec, h);
  535.             *e=t;
  536.             c=e;
  537.         }
  538.         }
  539.         *f='\0';
  540.         printf("%s", c);
  541.         *f='(';
  542.         idtest[4]=f-1;
  543.         c=f;
  544.         break; /* manpage */
  545.     case 3: /* ftp */
  546.     case 2: /* www */
  547.         g=f=idtest[j];
  548.         while (*g && (isalnum(*g) || *g=='_' || *g=='-' || *g=='+' ||
  549.               *g=='.')) g++;
  550.         if (g[-1]=='.') g--;
  551.         if (g-f>4) {
  552.         char t;
  553.         t=*f; *f='\0';
  554.         printf("%s",c);
  555.         *f=t; t=*g;*g='\0';
  556.         printf("<A HREF=\"%s://%s\">%s</A>", (j==3?"ftp":"http"),
  557.                f,f);
  558.         *g=t;
  559.         c=g;
  560.         } else {
  561.         f[3]='\0';
  562.         printf("%s",c);
  563.         c=f+3;
  564.         f[3]='.';
  565.         }
  566.         break;
  567.     case 1: /* mailto */
  568.         g=f=idtest[1];
  569.         while (g>c && (isalnum(g[-1]) || g[-1]=='_' || g[-1]=='-' ||
  570.                g[-1]=='+' || g[-1]=='.' || g[-1]=='%')) g--;
  571.         h=f+1;
  572.         while (*h && (isalnum(*h) || *h=='_' || *h=='-' || *h=='+' ||
  573.               *h=='.')) h++;
  574.         if (*h=='.') h--;
  575.         if (h-f>4 && f-g>1) {
  576.         char t;
  577.         t=*g;
  578.         *g='\0';
  579.         printf("%s",c);
  580.         *g=t;t=*h;*h='\0';
  581.         printf("<A HREF=\"mailto:%s\">%s</A>",g,g);
  582.         *h=t;
  583.         c=h;
  584.         } else {
  585.         *f='\0';
  586.         printf("%s",c);
  587.         *f='@';
  588.         idtest[1]=c;
  589.         c=f;
  590.         }
  591.         break;
  592.     case 0: /* url */
  593.         g=f=idtest[0];
  594.         while (g>c && isalpha(g[-1]) && islower(g[-1])) g--;
  595.         h=f+3;
  596.         while (*h && !isspace(*h) && *h!='<' && *h!='>' && *h!='"' &&
  597.            *h!='&') h++;
  598.         if (f-g>2 && f-g<7 && h-f>3) {
  599.         char t;
  600.         t=*g;
  601.         *g='\0';
  602.         printf("%s", c);
  603.         *g=t; t=*h; *h='\0';
  604.         printf("<A HREF=\"%s\">%s</A>", g,g);
  605.         *h=t;
  606.         c=h;
  607.         } else {
  608.         f[1]='\0';
  609.         printf("%s", c);
  610.         f[1]='/';
  611.         c=f+1;
  612.         }
  613.         break;
  614.     default:
  615.         break;
  616.     }
  617.     nr=0;
  618.     if (idtest[0] && idtest[0]<c) idtest[0]=strstr(c+1,"://");
  619.     if (idtest[1] && idtest[1]<c) idtest[1]=strchr(c+1,'@');
  620.     if (idtest[2] && idtest[2]<c) idtest[2]=strstr(c,"www.");
  621.     if (idtest[3] && idtest[3]<c) idtest[3]=strstr(c,"ftp.");
  622.     if (idtest[4] && idtest[4]<c) idtest[4]=strchr(c+1,'(');
  623.     if (idtest[5] && idtest[5]<c) idtest[5]=strstr(c+1,".h>");
  624.     for (i=0; i<6; i++) nr += (idtest[i]!=NULL);
  625.     }
  626.     printf("%s", c);
  627. }
  628.  
  629. int current_font=0;
  630. int current_size=0;
  631. int fillout=1;
  632.  
  633. void out_html(char *c)
  634. {
  635.   if (!c) return;
  636.   if (no_newline_output) {
  637.       int i=0;
  638.       no_newline_output=1;
  639.       while (c[i]) {
  640.       if (!no_newline_output) c[i-1]=c[i];
  641.       if (c[i]=='\n') no_newline_output=0;
  642.       i++;
  643.       }
  644.       if (!no_newline_output) c[i-1]=0;
  645.   }
  646.   if (scaninbuff) {
  647.       while (*c) {
  648.       if (buffpos>=buffmax) {
  649.           char *h;
  650.           h=realloc(buffer, buffmax*2);
  651.           if (!h) return;
  652.           buffer=h;
  653.           buffmax=buffmax*2;
  654.       }
  655.       buffer[buffpos++]=*c++;
  656.       }
  657.   } else
  658.       if (output_possible) {
  659.       while (*c) {
  660.           outbuffer[obp++]=*c;
  661.           if (*c=='\n' || obp>1000) {
  662.           outbuffer[obp]='\0';
  663.           add_links(outbuffer);
  664.           obp=0;
  665.           }
  666.           c++;
  667.       }
  668.       }
  669. }
  670.  
  671. #define FO0 ""
  672. #define FC0 ""
  673. #define FO1 "<I>"
  674. #define FC1 "</I>"
  675. #define FO2 "<B>"
  676. #define FC2 "</B>"
  677. #define FO3 "<TT>"
  678. #define FC3 "</TT>"
  679.  
  680. char *switchfont[16] = { ""     , FC0 FO1, FC0 FO2, FC0 FO3,
  681.              FC1 FO0, ""     , FC1 FO2, FC1 FO3,
  682.              FC2 FO0, FC2 FO1, ""     , FC2 FO3,
  683.              FC3 FO0, FC3 FO1, FC3 FO2, ""      };
  684.  
  685. char *change_to_font(int nr)
  686. {
  687.   int i;
  688.   switch (nr) {
  689.   case '0': nr++;
  690.   case '1': case '2': case '3': case '4': nr=nr-'1'; break;
  691.   case V('C','W'): nr=3; break;
  692.   case 'L': nr=3; break;
  693.   case 'B': nr=2; break;
  694.   case 'I': nr=1; break;
  695.   case 'P': case 'R': nr=0; break;
  696.   case 0: case 1: case 2: case 3: break;
  697.   default: nr=0; break;
  698.   }
  699.   i= current_font*4+nr%4;
  700.   current_font=nr%4;
  701.   return switchfont[i];
  702. }
  703.  
  704. static char sizebuf[200];
  705.  
  706. char *change_to_size(int nr)
  707. {
  708.   int i;
  709.   switch (nr) {
  710.   case '0': case '1': case '2': case '3': case '4': case '5': case '6':
  711.   case '7': case '8': case '9': nr=nr-'0'; break;
  712.   case '\0': break;
  713.   default: nr=current_size+nr; if (nr>9) nr=9; if (nr< -9) nr=-9; break;
  714.   }
  715.   if (nr==current_size) return "";
  716.   i=current_font;
  717.   sizebuf[0]='\0';
  718.   strcat(sizebuf, change_to_font(0));
  719.   if (current_size) strcat(sizebuf, "</FONT>");
  720.   current_size=nr;
  721.   if (nr) {
  722.     int l;
  723.     strcat(sizebuf, "<FONT SIZE=");
  724.     l=strlen(sizebuf);
  725.     if (nr>0) sizebuf[l++]='+'; else sizebuf[l++]='-',nr=-nr;
  726.     sizebuf[l++]=nr+'0';
  727.     sizebuf[l++]='>';
  728.     sizebuf[l]='\0';
  729.   }
  730.   strcat(sizebuf, change_to_font(i));
  731.   return sizebuf;
  732. }
  733.  
  734. int asint=0;
  735. int intresult=0;
  736.  
  737. #define SKIPEOL while (*c && *c++!='\n')
  738.  
  739. static int skip_escape=0;
  740. static int single_escape=0;
  741.  
  742. char *scan_escape(char *c)
  743. {
  744.     char *h=NULL;
  745.     char b[5];
  746.     INTDEF *intd;
  747.     int exoutputp,exskipescape;
  748.     int i,j;
  749.  
  750.     intresult=0;
  751.     switch (*c) {
  752.     case 'e': h="\\"; curpos++;break;
  753.     case '0':
  754.     case ' ': h=" ";curpos++; break;
  755.     case '|': h=""; break;
  756.     case '"': SKIPEOL; c--; h=""; break;
  757.     case '$':
  758.     if (argument) {
  759.         c++;
  760.         i=(*c -'1');
  761.         if (!(h=argument[i])) h="";
  762.     }
  763.     break;
  764.     case 'z':
  765.     c++;
  766.     if (*c=='\\') { c=scan_escape(c+1); c--;h=""; }
  767.     else {
  768.         b[0]=*c;
  769.         b[1]='\0';
  770.         h="";
  771.     }
  772.     break;
  773.     case 'k': c++; if (*c=='(') c+=2;
  774.     case '^':
  775.     case '!':
  776.     case '%':
  777.     case 'a':
  778.     case 'd':
  779.     case 'r':
  780.     case 'u':
  781.     case '\n':
  782.     case '&': h=""; break;
  783.     case '(':
  784.     c++;
  785.     i= c[0]*256+c[1];
  786.     c++;
  787.     h = expand_char(i);
  788.     break;
  789.     case '*':
  790.     c++;
  791.     if (*c=='(') {
  792.         c++;
  793.         i= c[0]*256+c[1];
  794.         c++;
  795.     } else
  796.         i= *c *256+' ';
  797.     h = expand_string(i);
  798.     break;
  799.     case 'f':
  800.     c++;    
  801.     if (*c=='\\') {
  802.         c++;
  803.         c=scan_escape(c);
  804.         c--;
  805.         i=intresult;
  806.     } else     if (*c != '(')
  807.         i=*c;
  808.     else {
  809.         c++;
  810.         i=c[0]*256+c[1];
  811.         c++;
  812.     }
  813.     if (!skip_escape) h=change_to_font(i); else h="";
  814.     break;
  815.     case 's':
  816.     c++;
  817.     j=0;i=0;
  818.     if (*c=='-') {j= -1; c++;} else if (*c=='+') {j=1; c++;}
  819.     if (*c=='0') c++; else if (*c=='\\') {
  820.         c++;
  821.         c=scan_escape(c);
  822.         i=intresult; if (!j) j=1;
  823.     } else
  824.         while (isdigit(*c) && (!i || (!j && i<4))) i=i*10+(*c++)-'0';
  825.     if (!j) { j=1; if (i) i=i-10; }
  826.     if (!skip_escape) h=change_to_size(i*j); else h="";
  827.     c--;
  828.     break;
  829.     case 'n':
  830.     c++;
  831.     j=0;
  832.     switch (*c) {
  833.     case '+': j=1; c++; break;
  834.     case '-': j=-1; c++; break;
  835.     default: break;
  836.     }
  837.     if (*c=='(') {
  838.         c++;
  839.         i=V(c[0],c[1]);
  840.         c=c+1;
  841.     } else {
  842.         i=V(c[0],' ');
  843.     }
  844.     intd=intdef;
  845.     while (intd && intd->nr!=i) intd=intd->next;
  846.     if (intd) {
  847.         intd->val=intd->val+j*intd->incr;
  848.         intresult=intd->val;
  849.     } else {
  850.         switch (i) {
  851.         case V('.','s'): intresult=current_size; break;
  852.         case V('.','f'): intresult=current_font; break;
  853.         default: intresult=0; break;
  854.         }
  855.     }
  856.     h="";
  857.     break;
  858.     case 'w':
  859.     c++;
  860.     i=*c;
  861.     c++;
  862.     exoutputp=output_possible;
  863.     exskipescape=skip_escape;
  864.     output_possible=0;
  865.     skip_escape=1;
  866.     j=0;
  867.     while (*c!=i) {
  868.         j++;
  869.         if (*c==escapesym) c=scan_escape(c+1); else c++;
  870.     }
  871.     output_possible=exoutputp;
  872.     skip_escape=exskipescape;
  873.     intresult=j;
  874.     break;
  875.     case 'l': h="<HR>"; curpos=0;
  876.     case 'b':
  877.     case 'v':
  878.     case 'x':
  879.     case 'o':
  880.     case 'L':
  881.     case 'h':
  882.     c++;
  883.     i=*c;
  884.     c++;
  885.     exoutputp=output_possible;
  886.     exskipescape=skip_escape;
  887.     output_possible=0;
  888.     skip_escape=1;
  889.     while (*c != i)
  890.         if (*c==escapesym) c=scan_escape(c+1);
  891.         else c++;
  892.     output_possible=exoutputp;
  893.     skip_escape=exskipescape;
  894.     break;
  895.     case 'c': no_newline_output=1; break;
  896.     case '{': newline_for_fun++; h="";break;
  897.     case '}': if (newline_for_fun) newline_for_fun--; h="";break;
  898.     case 'p': h="<BR>\n";curpos=0; break;
  899.     case 't': h="\t";curpos=(curpos+8)&0xfff8; break;
  900.     case '<': h="<";curpos++; break;
  901.     case '>': h=">";curpos++; break;
  902.     case '\\': if (single_escape) { c--; break;}
  903.     default: b[0]=*c; b[1]=0; h=b; curpos++; break;
  904.     }
  905.     c++;
  906.     if (!skip_escape) out_html(h);
  907.     return c;
  908. }
  909.  
  910. typedef struct TABLEITEM TABLEITEM;
  911.  
  912. struct TABLEITEM {
  913.     char *contents;
  914.     int size,align,valign,colspan,rowspan,font,vleft,vright,space,width;
  915.     TABLEITEM *next;
  916. };
  917.  
  918. static TABLEITEM emptyfield = {NULL,0,0,0,1,1,0,0,0,0,0,NULL};
  919. typedef struct TABLEROW TABLEROW;
  920.  
  921. struct TABLEROW {
  922.     TABLEITEM *first;
  923.     TABLEROW *prev, *next;
  924. };
  925.  
  926. static char *tableopt[]= { "center", "expand", "box", "allbox", "doublebox",
  927.                "tab", "linesize", "delim", NULL };
  928. static int tableoptl[] = { 6,6,3,6,9,3,8,5,0};
  929.  
  930.  
  931. static void clear_table(TABLEROW *table)
  932. {
  933.     TABLEROW *tr1,*tr2;
  934.     TABLEITEM *ti1,*ti2;
  935.  
  936.     tr1=table;
  937.     while (tr1->prev) tr1=tr1->prev;
  938.     while (tr1) {
  939.     ti1=tr1->first;
  940.     while (ti1) {
  941.         ti2=ti1->next;
  942.         if (ti1->contents) free(ti1->contents);
  943.         free(ti1);
  944.         ti1=ti2;
  945.     }
  946.     tr2=tr1;
  947.     tr1=tr1->next;
  948.     free(tr2);
  949.     }
  950. }
  951.  
  952. char *scan_expression(char *c, int *result);
  953.  
  954. static char *scan_format(char *c, TABLEROW **result, int *maxcol)
  955. {
  956.     TABLEROW *layout, *currow;
  957.     TABLEITEM *curfield;
  958.     int i,j;
  959.     if (*result) {
  960.     clear_table(*result);
  961.     }
  962.     layout= currow=(TABLEROW*) malloc(sizeof(TABLEROW));
  963.     currow->next=currow->prev=NULL;
  964.     currow->first=curfield=(TABLEITEM*) malloc(sizeof(TABLEITEM));
  965.     *curfield=emptyfield;
  966.     while (*c && *c!='.') {
  967.     switch (*c) {
  968.     case 'C': case 'c': case 'N': case 'n':
  969.     case 'R': case 'r': case 'A': case 'a':
  970.     case 'L': case 'l': case 'S': case 's':
  971.     case '^': case '_':
  972.         if (curfield->align) {
  973.         curfield->next=(TABLEITEM*)malloc(sizeof(TABLEITEM));
  974.         curfield=curfield->next;
  975.         *curfield=emptyfield;
  976.         }
  977.         curfield->align=toupper(*c);
  978.         c++;
  979.         break;
  980.     case 'i': case 'I': case 'B': case 'b':
  981.         curfield->font = toupper(*c);
  982.         c++;
  983.         break;
  984.     case 'f': case 'F':
  985.         c++;
  986.         curfield->font = toupper(*c);
  987.         c++;
  988.         if (!isspace(*c)) c++;
  989.         break;
  990.     case 't': case 'T': curfield->valign='t'; c++; break;
  991.     case 'p': case 'P':
  992.         c++;
  993.         i=j=0;
  994.         if (*c=='+') { j=1; c++; }
  995.         if (*c=='-') { j=-1; c++; }
  996.         while (isdigit(*c)) i=i*10+(*c++)-'0';
  997.         if (j) curfield->size= i*j; else curfield->size=j-10;
  998.         break;
  999.     case 'v': case 'V':
  1000.     case 'w': case 'W':
  1001.         c=scan_expression(c+2,&curfield->width);
  1002.         break;
  1003.     case '|':
  1004.         if (curfield->align) curfield->vleft++;
  1005.         else curfield->vright++;
  1006.         c++;
  1007.         break;
  1008.     case 'e': case 'E':
  1009.         c++;
  1010.         break;
  1011.     case '0': case '1': case '2': case '3': case '4':
  1012.     case '5': case '6': case '7': case '8': case '9':
  1013.         i=0;
  1014.         while (isdigit(*c)) i=i*10+(*c++)-'0';
  1015.         curfield->space=i;
  1016.         break;
  1017.     case ',': case '\n':
  1018.         currow->next=(TABLEROW*)malloc(sizeof(TABLEROW));
  1019.         currow->next->prev=currow;
  1020.         currow=currow->next;
  1021.         currow->next=NULL;
  1022.         curfield=currow->first=(TABLEITEM*)malloc(sizeof(TABLEITEM));
  1023.         *curfield=emptyfield;
  1024.         c++;
  1025.         break;
  1026.     default:
  1027.         c++;
  1028.         break;
  1029.     }
  1030.     }
  1031.     if (*c=='.') while (*c++!='\n');
  1032.     *maxcol=0;
  1033.     currow=layout;
  1034.     while (currow) {
  1035.     curfield=layout->first;
  1036.     i=0;
  1037.     while (curfield) {
  1038.         i++;
  1039.         curfield=curfield->next;
  1040.     }
  1041.     if (i>*maxcol) *maxcol=i;
  1042.     currow=currow->next;
  1043.     }
  1044.     *result=layout;
  1045.     return c;
  1046. }
  1047.  
  1048. TABLEROW *next_row(TABLEROW *tr)
  1049. {
  1050.     if (tr->next) {
  1051.     tr=tr->next;
  1052.     if (!tr->next) next_row(tr);
  1053.     return tr;
  1054.     } else {
  1055.     TABLEITEM *ti, *ti2;
  1056.     tr->next=(TABLEROW*)malloc(sizeof(TABLEROW));
  1057.     tr->next->prev=tr;
  1058.     ti=tr->first;
  1059.     tr=tr->next;
  1060.     tr->next=NULL;
  1061.     if (ti) tr->first=ti2=(TABLEITEM*) malloc(sizeof(TABLEITEM));
  1062.     else tr->first=ti2=NULL;
  1063.     while (ti!=ti2) {
  1064.         *ti2=*ti;
  1065.         ti2->contents=NULL;
  1066.         if ((ti=ti->next)) {
  1067.         ti2->next=(TABLEITEM*) malloc(sizeof(TABLEITEM));
  1068.         }
  1069.         ti2=ti2->next;
  1070.     }
  1071.     return tr;
  1072.     }
  1073. }
  1074.  
  1075. char itemreset[20]="\\fR\\s0";
  1076.  
  1077. char *scan_table(char *c)
  1078. {
  1079.     char *t, *h, *g;
  1080.     int center=0, expand=0, box=0, border=0, linesize=1;
  1081.     int i,j,maxcol=0, finished=0;
  1082.     int oldfont, oldsize,oldfillout;
  1083.     char itemsep='\t';
  1084.     TABLEROW *layout=NULL, *currow, *ftable;
  1085.     TABLEITEM *curfield;
  1086.     while (*c++!='\n');
  1087.     h=c;
  1088.     if (*h=='.') return c-1;
  1089.     oldfont=current_font;
  1090.     oldsize=current_size;
  1091.     oldfillout=fillout;
  1092.     out_html(change_to_font(0));
  1093.     out_html(change_to_size(0));
  1094.     if (!fillout) {
  1095.     fillout=1;
  1096.     out_html("</PRE>");
  1097.     }
  1098.     while (*h && *h!='\n') h++;
  1099.     if (h[-1]==';') {
  1100.     /* scan table options */
  1101.     while (c<h) {
  1102.         while (isspace(*c)) c++;
  1103.         for (i=0; tableopt[i] && strncmp(tableopt[i],c,tableoptl[i]);i++);
  1104.         c=c+tableoptl[i];
  1105.         switch (i) {
  1106.         case 0: center=1; break;
  1107.         case 1: expand=1; break;
  1108.         case 2: box=1; break;
  1109.         case 3: border=1; break;
  1110.         case 4: box=2; break;
  1111.         case 5: while (*c++!='('); itemsep=*c++; break;
  1112.         case 6: while (*c++!='('); linesize=0;
  1113.         while (isdigit(*c)) linesize=linesize*10+(*c++)-'0';
  1114.         break;
  1115.         case 7: while (*c!=')') c++;
  1116.         default: break;
  1117.         }
  1118.         c++;
  1119.     }
  1120.     c=h+1;
  1121.     }
  1122.     /* scan layout */
  1123.     c=scan_format(c,&layout, &maxcol);
  1124.     currow=layout;
  1125.     next_row(currow);
  1126.     curfield=layout->first;
  1127.     i=0;
  1128.     while (!finished) {
  1129.     /* search item */
  1130.     h=c;
  1131.     if ((*c=='_' || *c=='=') && (c[1]==itemsep || c[1]=='\n')) {
  1132.         if (c[-1]=='\n' && c[1]=='\n') {
  1133.         if (currow->prev) {
  1134.             currow->prev->next=(TABLEROW*) malloc(sizeof(TABLEROW));
  1135.             currow->prev->next->next=currow;
  1136.             currow->prev->next->prev=currow->prev;
  1137.             currow->prev=currow->prev->next;
  1138.         } else {
  1139.             currow->prev=layout=(TABLEROW*) malloc(sizeof(TABLEROW));
  1140.             currow->prev->prev=NULL;
  1141.             currow->prev->next=currow;
  1142.         }
  1143.         curfield=currow->prev->first=
  1144.             (TABLEITEM*) malloc(sizeof(TABLEITEM));
  1145.         *curfield=emptyfield;
  1146.         curfield->align=*c;
  1147.         curfield->colspan=maxcol;
  1148.         curfield=currow->first;
  1149.         c=c+2;
  1150.         } else {
  1151.         if (curfield) {
  1152.             curfield->align=*c;
  1153.             do {
  1154.             curfield=curfield->next;
  1155.             } while (curfield && curfield->align=='S');
  1156.         }
  1157.         if (c[1]=='\n') {
  1158.             currow=next_row(currow);
  1159.             curfield=currow->first;
  1160.         }
  1161.         c=c+2;
  1162.         }
  1163.     } else if (*c=='T' && c[1]=='{') {
  1164.         h=c+2;
  1165.         c=strstr(h,"\nT}");
  1166.         c++;
  1167.         *c='\0';
  1168.         g=NULL;
  1169.         scan_troff(h,0,&g);
  1170.         scan_troff(itemreset, 0,&g);
  1171.         *c='T';
  1172.         c+=3;
  1173.         if (curfield) {
  1174.         curfield->contents=g;
  1175.         do {
  1176.             curfield=curfield->next;
  1177.         } while (curfield && curfield->align=='S');
  1178.         } else
  1179.         if (g) free(g);
  1180.         if (c[-1]=='\n') {
  1181.         currow=next_row(currow);
  1182.         curfield=currow->first;
  1183.         }
  1184.     } else if (*c=='.' && c[1]=='T' && c[2]=='&' && c[-1]=='\n') {
  1185.         TABLEROW *hr;
  1186.         while (*c++!='\n');
  1187.         hr=currow;
  1188.         currow=currow->prev;
  1189.         hr->prev=NULL;
  1190.         c=scan_format(c,&hr, &i);
  1191.         hr->prev=currow;
  1192.         currow->next=hr;
  1193.         currow=hr;
  1194.         next_row(currow);
  1195.         curfield=currow->first;
  1196.     } else if (*c=='.' && c[1]=='T' && c[2]=='E' && c[-1]=='\n') {
  1197.         finished=1;
  1198.         while (*c++!='\n');
  1199.         if (currow->prev)
  1200.         currow->prev->next=NULL;
  1201.         currow->prev=NULL;
  1202.         clear_table(currow);
  1203.     } else if (*c=='.' && c[-1]=='\n' && !isdigit(c[1])) {
  1204.         /* skip troff request inside table (usually only .sp ) */
  1205.         while (*c++!='\n');
  1206.     } else {
  1207.         h=c;
  1208.         while (*c && (*c!=itemsep || c[-1]=='\\') &&
  1209.            (*c!='\n' || c[-1]=='\\')) c++;
  1210.         i=0;
  1211.         if (*c==itemsep) {i=1; *c='\n'; }
  1212.         if (h[0]=='\\' && h[2]=='\n' &&
  1213.         (h[1]=='_' || h[1]=='^')) {
  1214.         if (curfield) {
  1215.             curfield->align=h[1];
  1216.             do {
  1217.             curfield=curfield->next;
  1218.             } while (curfield && curfield->align=='S');
  1219.         }
  1220.         h=h+3;
  1221.         } else {
  1222.         g=NULL;
  1223.         h=scan_troff(h,1,&g);
  1224.         scan_troff(itemreset,0,&g);
  1225.         if (curfield) {
  1226.             curfield->contents=g;
  1227.             do {
  1228.             curfield=curfield->next;
  1229.             } while (curfield && curfield->align=='S');
  1230.         } else if (g) free(g);
  1231.         }
  1232.         if (i) *c=itemsep;
  1233.         c=h;
  1234.         if (c[-1]=='\n') {
  1235.         currow=next_row(currow);
  1236.         curfield=currow->first;
  1237.         }
  1238.     }
  1239.     }
  1240.     /* calculate colspan and rowspan */
  1241.     currow=layout;
  1242.     while (currow->next) currow=currow->next;
  1243.     while (currow) {
  1244.     TABLEITEM *ti, *ti1=NULL, *ti2=NULL;
  1245.     ti=currow->first;
  1246.     if (currow->prev) ti1=currow->prev->first;
  1247.     while (ti) {
  1248.         switch (ti->align) {
  1249.         case 'S':
  1250.         if (ti2) {
  1251.             ti2->colspan++;
  1252.             if (ti2->rowspan<ti->rowspan) ti2->rowspan=ti->rowspan;
  1253.         }
  1254.         break;
  1255.         case '^':
  1256.         if (ti1) ti1->rowspan++;
  1257.         default:
  1258.         if (!ti2) ti2=ti;
  1259.         else {
  1260.             do {
  1261.             ti2=ti2->next;
  1262.             } while (ti2 && curfield->align=='S');
  1263.         }
  1264.         break;
  1265.         }
  1266.         ti=ti->next;
  1267.         if (ti1) ti1=ti1->next;
  1268.     }
  1269.     currow=currow->prev;
  1270.     }
  1271.     /* produce html output */
  1272.     if (center) out_html("<CENTER>");
  1273.     if (box==2) out_html("<TABLE BORDER><TR><TD>");
  1274.     out_html("<TABLE");
  1275.     if (box || border) {
  1276.     out_html(" BORDER");
  1277.     if (!border) out_html("><TR><TD><TABLE");
  1278.     if (expand) out_html(" WIDTH=100%");
  1279.     }
  1280.     out_html(">\n");
  1281.     currow=layout;
  1282.     while (currow) {
  1283.     j=0;
  1284.     out_html("<TR VALIGN=top>");
  1285.     curfield=currow->first;
  1286.     while (curfield) {
  1287.         if (curfield->align!='S' && curfield->align!='^') {
  1288.         out_html("<TD");
  1289.         switch (curfield->align) {
  1290.         case 'N':
  1291.             curfield->space+=4;
  1292.         case 'R':
  1293.             out_html(" ALIGN=right");
  1294.             break;
  1295.         case 'C':
  1296.             out_html(" ALIGN=center");
  1297.         default:
  1298.             break;
  1299.         }
  1300.         if (!curfield->valign && curfield->rowspan>1)
  1301.             out_html(" VALIGN=center");
  1302.         if (curfield->colspan>1) {
  1303.             char buf[5];
  1304.             out_html(" COLSPAN=");
  1305.             sprintf(buf, "%i", curfield->colspan);
  1306.             out_html(buf);
  1307.         }
  1308.         if (curfield->rowspan>1) {
  1309.             char buf[5];
  1310.             out_html(" ROWSPAN=");
  1311.             sprintf(buf, "%i", curfield->rowspan);
  1312.             out_html(buf);
  1313.         }
  1314.         j=j+curfield->colspan;
  1315.         out_html(">");
  1316.         if (curfield->size) out_html(change_to_size(curfield->size));
  1317.         if (curfield->font) out_html(change_to_font(curfield->font));
  1318.         switch (curfield->align) {
  1319.         case '=': out_html("<HR><HR>"); break;
  1320.         case '_': out_html("<HR>"); break;
  1321.         default:
  1322.             if (curfield->contents) out_html(curfield->contents);
  1323.             break;
  1324.         }
  1325.         if (curfield->space)
  1326.             for (i=0; i<curfield->space;i++) out_html(" ");
  1327.         if (curfield->font) out_html(change_to_font(0));
  1328.         if (curfield->size) out_html(change_to_size(0));
  1329.         if (j>=maxcol && curfield->align>'@' && curfield->align!='_')
  1330.             out_html("<BR>");
  1331.         out_html("</TD>");
  1332.         }
  1333.         curfield=curfield->next;
  1334.     }
  1335.     out_html("</TR>\n");
  1336.     currow=currow->next;
  1337.     }
  1338.     if (box && !border) out_html("</TABLE>");
  1339.     out_html("</TABLE>");
  1340.     if (box==2) out_html("</TABLE>");
  1341.     if (center) out_html("</CENTER>\n");
  1342.     else out_html("\n");
  1343.     if (!oldfillout) out_html("<PRE>");
  1344.     fillout=oldfillout;
  1345.     out_html(change_to_size(oldsize));
  1346.     out_html(change_to_font(oldfont));
  1347.     return c;
  1348. }
  1349.  
  1350. char *scan_expression(char *c, int *result)
  1351. {
  1352.     int value=0,value2,j=0,sign=1,opex=0;
  1353.     char oper='c';
  1354.  
  1355.     if (*c=='!') {
  1356.     c=scan_expression(c+1, &value);
  1357.     value= (!value);
  1358.     } else if (*c=='n') {
  1359.     c++;
  1360.     value=NROFF;
  1361.     } else if (*c=='t') {
  1362.     c++;
  1363.     value=1-NROFF;
  1364.     } else if (*c=='\'' || *c=='"' || *c<' ' || (*c=='\\' && c[1]=='(')) {
  1365.     /* ?string1?string2?
  1366.     ** test if string1 equals string2.
  1367.     */
  1368.     char *st1=NULL, *st2=NULL, *h;
  1369.     char *tcmp=NULL;
  1370.     char sep;
  1371.     sep=*c;
  1372.     if (sep=='\\') {
  1373.         tcmp=c;
  1374.         c=c+3;
  1375.     }
  1376.     c++;
  1377.     h=c;
  1378.     while (*c!= sep && (!tcmp || strncmp(c,tcmp,4))) c++;
  1379.     *c='\n';
  1380.     scan_troff(h, 1, &st1);
  1381.     *c=sep;
  1382.     if (tcmp) c=c+3;
  1383.     c++;
  1384.     h=c;
  1385.     while (*c!=sep && (!tcmp || strncmp(c,tcmp,4))) c++;
  1386.     *c='\n';
  1387.     scan_troff(h,1,&st2);
  1388.     *c=sep;
  1389.     if (!st1 && !st2) value=1;
  1390.     else if (!st1 || !st2) value=0;
  1391.     else value=(!strcmp(st1, st2));
  1392.     if (st1) free(st1);
  1393.     if (st2) free(st2);
  1394.     if (tcmp) c=c+3;
  1395.     c++;
  1396.     } else {
  1397.     while (*c && !isspace(*c) && *c!=')') {
  1398.         opex=0;
  1399.         switch (*c) {
  1400.         case '(':
  1401.         c=scan_expression(c+1, &value2);
  1402.         value2=sign*value2;
  1403.         opex=1;
  1404.         break;
  1405.         case '.':
  1406.         case '0': case '1':
  1407.         case '2': case '3':
  1408.         case '4': case '5':
  1409.         case '6': case '7':
  1410.         case '8': case '9': {
  1411.         int num=0,denum=1;
  1412.         value2=0;
  1413.         while (isdigit(*c)) value2=value2*10+((*c++)-'0');
  1414.         if (*c=='.') {
  1415.             c++;
  1416.             while (isdigit(*c)) {
  1417.             num=num*10+((*c++)-'0');
  1418.             denum=denum*10;
  1419.             }
  1420.         }
  1421.         if (isalpha(*c)) {
  1422.             /* scale indicator */
  1423.             switch (*c) {
  1424.             case 'i': /* inch -> 10pt */
  1425.             value2=value2*10+(num*10+denum/2)/denum;
  1426.             num=0;
  1427.             break;
  1428.             default:
  1429.             break;
  1430.             }
  1431.             c++;
  1432.         }
  1433.         value2=value2+(num+denum/2)/denum;
  1434.         value2=sign*value2;
  1435.         opex=1;
  1436.         break;
  1437.         }
  1438.         case '\\':
  1439.         c=scan_escape(c+1);
  1440.         value2=intresult*sign;
  1441.         if (isalpha(*c)) c++; /* scale indicator */
  1442.         opex=1;
  1443.         break;
  1444.         case '-':
  1445.         if (oper) { sign=-1; c++; break; }
  1446.         case '>':
  1447.         case '<':
  1448.         case '+':
  1449.         case '/':
  1450.         case '*':
  1451.         case '%':
  1452.         case '&':
  1453.         case '=':
  1454.         case ':':
  1455.         if (c[1]=='=') oper=(*c++) +16; else oper=*c;
  1456.         c++;
  1457.         break;
  1458.         default: c++; break;
  1459.         }
  1460.         if (opex) {
  1461.         sign=1;
  1462.         switch (oper) {
  1463.         case 'c': value=value2; break;
  1464.         case '-': value=value-value2; break;
  1465.         case '+': value=value+value2; break;
  1466.         case '*': value=value*value2; break;
  1467.         case '/': if (value2) value=value/value2; break;
  1468.         case '%': if (value2) value=value%value2; break;
  1469.         case '<': value=(value<value2); break;
  1470.         case '>': value=(value>value2); break;
  1471.         case '>'+16: value=(value>=value2); break;
  1472.         case '<'+16: value=(value<=value2); break;
  1473.         case '=': case '='+16: value=(value==value2); break;
  1474.         case '&': value = (value && value2); break;
  1475.         case ':': value = (value || value2); break;
  1476.         default: fprintf(stderr, "Unknown operator %c.\n", oper);
  1477.         }
  1478.         oper=0;
  1479.         }
  1480.     }
  1481.     if (*c==')') c++;
  1482.     }
  1483.     *result=value;
  1484.     return c;
  1485. }
  1486.  
  1487. void trans_char(char *c, char s, char t)
  1488. {
  1489.     char *sl=c;
  1490.     int slash=0;
  1491.     while (*sl!='\n' || slash) {
  1492.     if (!slash) {
  1493.         if (*sl==escapesym)
  1494.         slash=1;
  1495.         else if (*sl==s)
  1496.         *sl=t;
  1497.     } else slash=0;
  1498.     sl++;
  1499.     }
  1500. }
  1501.  
  1502. char *fill_words(char *c, char *words[], int *n)
  1503. {
  1504.     char *sl=c;
  1505.     int slash=0;
  1506.     int skipspace=0;
  1507.     *n=0;
  1508.     words[*n]=sl;
  1509.     while (*sl && (*sl!='\n' || slash)) {
  1510.     if (!slash) {
  1511.         if (*sl=='"') {
  1512.         *sl='\a';
  1513.         skipspace=!skipspace;
  1514.         } else if (*sl==escapesym)
  1515.         slash=1;
  1516.         else if ((*sl==' ' || *sl=='\t') && !skipspace) {
  1517.         *sl='\n';
  1518.         if (words[*n]!=sl) (*n)++;
  1519.         words[*n]=sl+1;
  1520.         }
  1521.     } else {
  1522.         if (*sl=='"') {
  1523.         sl--;
  1524.         *sl='\n';
  1525.         if (words[*n]!=sl) (*n)++;
  1526.         sl++;
  1527.         while (*sl && *sl !='\n') sl++;
  1528.         words[*n]=sl;
  1529.         sl--;
  1530.         }
  1531.         slash=0;
  1532.     }
  1533.     sl++;
  1534.     }
  1535.     if (sl!=words[*n]) (*n)++;
  1536.     return sl;
  1537. }
  1538.  
  1539. char *abbrev_list[] = {
  1540.     "GSBG", "Getting Started ",
  1541.     "SUBG", "Customizing SunOS",
  1542.     "SHBG", "Basic Troubleshooting",
  1543.     "SVBG", "SunView User's Guide",
  1544.     "MMBG", "Mail and Messages",
  1545.     "DMBG", "Doing More with SunOS",
  1546.     "UNBG", "Using the Network",
  1547.     "GDBG", "Games, Demos & Other Pursuits",
  1548.     "CHANGE", "SunOS 4.1 Release Manual",
  1549.     "INSTALL", "Installing SunOS 4.1",
  1550.     "ADMIN", "System and Network Administration",
  1551.     "SECUR", "Security Features Guide",
  1552.     "PROM", "PROM User's Manual",
  1553.     "DIAG", "Sun System Diagnostics",
  1554.     "SUNDIAG", "Sundiag User's Guide",
  1555.     "MANPAGES", "SunOS Reference Manual",
  1556.     "REFMAN", "SunOS Reference Manual",
  1557.     "SSI", "Sun System Introduction",
  1558.     "SSO", "System Services Overview",
  1559.     "TEXT", "Editing Text Files",
  1560.     "DOCS", "Formatting Documents",
  1561.     "TROFF", "Using <B>nroff</B> and <B>troff</B>",
  1562.     "INDEX", "Global Index",
  1563.     "CPG", "C Programmer's Guide",
  1564.     "CREF", "C Reference Manual",
  1565.     "ASSY", "Assembly Language Reference",
  1566.     "PUL", "Programming Utilities and Libraries",
  1567.     "DEBUG", "Debugging Tools",
  1568.     "NETP", "Network Programming",
  1569.     "DRIVER", "Writing Device Drivers",
  1570.     "STREAMS", "STREAMS Programming",
  1571.     "SBDK", "SBus Developer's Kit",
  1572.     "WDDS", "Writing Device Drivers for the SBus",
  1573.     "FPOINT", "Floating-Point Programmer's Guide",
  1574.     "SVPG", "SunView 1 Programmer's Guide",
  1575.     "SVSPG", "SunView 1 System Programmer's Guide",
  1576.     "PIXRCT", "Pixrect Reference Manual",
  1577.     "CGI", "SunCGI Reference Manual",
  1578.     "CORE", "SunCore Reference Manual",
  1579.     "4ASSY", "Sun-4 Assembly Language Reference",
  1580.     "SARCH", "<FONT SIZE=-1>SPARC</FONT> Architecture Manual",
  1581.     "KR", "The C Programming Language",
  1582.     NULL, NULL };
  1583.  
  1584. char *lookup_abbrev(char *c)
  1585. {
  1586.     int i=0;
  1587.  
  1588.     if (!c) return "";
  1589.     while (abbrev_list[i] && strcmp(c,abbrev_list[i])) i=i+2;
  1590.     if (abbrev_list[i]) return abbrev_list[i+1];
  1591.     else return c;
  1592. }
  1593.  
  1594. char *section_list[] = {
  1595.     "1", "User Commands ",
  1596.     "1C", "User Commands",
  1597.     "1G", "User Commands",
  1598.     "1S", "User Commands",
  1599.     "1V", "User Commands ",
  1600.     "2", "System Calls",
  1601.     "2V", "System Calls",
  1602.     "3", "C Library Functions",
  1603.     "3C", "Compatibility Functions",
  1604.     "3F", "Fortran Library Routines",
  1605.     "3K", "Kernel VM Library Functions",
  1606.     "3L", "Lightweight Processes Library",
  1607.     "3M", "Mathematical Library",
  1608.     "3N", "Network Functions",
  1609.     "3R", "RPC Services Library",
  1610.     "3S", "Standard I/O Functions",
  1611.     "3V", "C Library Functions",
  1612.     "3X", "Miscellaneous Library Functions",
  1613.     "4", "Devices and Network Interfaces",
  1614.     "4F", "Protocol Families",
  1615.     "4I", "Devices and Network Interfaces",
  1616.     "4M", "Devices and Network Interfaces",
  1617.     "4N", "Devices and Network Interfaces",
  1618.     "4P", "Protocols",
  1619.     "4S", "Devices and Network Interfaces",
  1620.     "4V", "Devices and Network Interfaces",
  1621.     "5", "File Formats",
  1622.     "5V", "File Formats",
  1623.     "6", "Games and Demos",
  1624.     "7", "Environments, Tables, and Troff Macros",
  1625.     "7V", "Environments, Tables, and Troff Macros",
  1626.     "8", "Maintenance Commands",
  1627.     "8C", "Maintenance Commands",
  1628.     "8S", "Maintenance Commands",
  1629.     "8V", "Maintenance Commands",
  1630.     "L", "Local Commands",
  1631. /* for Solaris: 
  1632.     "1", "User Commands",
  1633.     "1B", "SunOS/BSD Compatibility Package Commands",
  1634.     "1b", "SunOS/BSD Compatibility Package Commands",
  1635.     "1C", "Communication Commands ",
  1636.     "1c", "Communication Commands",
  1637.     "1F", "FMLI Commands ",
  1638.     "1f", "FMLI Commands",
  1639.     "1G", "Graphics and CAD Commands ",
  1640.     "1g", "Graphics and CAD Commands ",
  1641.     "1M", "Maintenance Commands",
  1642.     "1m", "Maintenance Commands",
  1643.     "1S", "SunOS Specific Commands",
  1644.     "1s", "SunOS Specific Commands",
  1645.     "2", "System Calls",
  1646.     "3", "C Library Functions",
  1647.     "3B", "SunOS/BSD Compatibility Library Functions",
  1648.     "3b", "SunOS/BSD Compatibility Library Functions",
  1649.     "3C", "C Library Functions",
  1650.     "3c", "C Library Functions",
  1651.     "3E", "C Library Functions",
  1652.     "3e", "C Library Functions",
  1653.     "3F", "Fortran Library Routines",
  1654.     "3f", "Fortran Library Routines",
  1655.     "3G", "C Library Functions",
  1656.     "3g", "C Library Functions",
  1657.     "3I", "Wide Character Functions",
  1658.     "3i", "Wide Character Functions",
  1659.     "3K", "Kernel VM Library Functions",
  1660.     "3k", "Kernel VM Library Functions",
  1661.     "3L", "Lightweight Processes Library",
  1662.     "3l", "Lightweight Processes Library",
  1663.     "3M", "Mathematical Library",
  1664.     "3m", "Mathematical Library",
  1665.     "3N", "Network Functions",
  1666.     "3n", "Network Functions",
  1667.     "3R", "Realtime Library",
  1668.     "3r", "Realtime Library",
  1669.     "3S", "Standard I/O Functions",
  1670.     "3s", "Standard I/O Functions",
  1671.     "3T", "Threads Library",
  1672.     "3t", "Threads Library",
  1673.     "3W", "C Library Functions",
  1674.     "3w", "C Library Functions",
  1675.     "3X", "Miscellaneous Library Functions",
  1676.     "3x", "Miscellaneous Library Functions",
  1677.     "4", "File Formats",
  1678.     "4B", "SunOS/BSD Compatibility Package File Formats",
  1679.     "4b", "SunOS/BSD Compatibility Package File Formats",
  1680.     "5", "Headers, Tables, and Macros",
  1681.     "6", "Games and Demos",
  1682.     "7", "Special Files",
  1683.     "7B", "SunOS/BSD Compatibility Special Files",
  1684.     "7b", "SunOS/BSD Compatibility Special Files",
  1685.     "8", "Maintenance Procedures",
  1686.     "8C", "Maintenance Procedures",
  1687.     "8c", "Maintenance Procedures",
  1688.     "8S", "Maintenance Procedures",
  1689.     "8s", "Maintenance Procedures",
  1690.     "9", "DDI and DKI",
  1691.     "9E", "DDI and DKI Driver Entry Points",
  1692.     "9e", "DDI and DKI Driver Entry Points",
  1693.     "9F", "DDI and DKI Kernel Functions",
  1694.     "9f", "DDI and DKI Kernel Functions",
  1695.     "9S", "DDI and DKI Data Structures",
  1696.     "9s", "DDI and DKI Data Structures",
  1697.     "L", "Local Commands",
  1698. */
  1699.     NULL, "Misc. Reference Manual Pages",
  1700.     NULL, NULL
  1701. };
  1702.  
  1703. char *section_name(char *c)
  1704. {
  1705.     int i=0;
  1706.  
  1707.     if (!c) return "";
  1708.     while (section_list[i] && strcmp(c,section_list[i])) i=i+2;
  1709.     if (section_list[i+1]) return section_list[i+1];
  1710.     else return c;
  1711. }
  1712.  
  1713. char manidx[20000];
  1714. int subs=0;
  1715. int mip=0;
  1716. char label[5]="lbAA";
  1717.  
  1718. void add_to_index(int level, char *item)
  1719. {
  1720.     char *c=NULL;
  1721.     label[3]++;
  1722.     if (label[3]>'Z') {
  1723.     label[3]='A';
  1724.     label[2]++;
  1725.     }
  1726.     if (level != subs) {
  1727.     if (subs) {
  1728.         strcpy(manidx+mip, "</DL>\n");
  1729.         mip+=6;
  1730.     } else {
  1731.         strcpy(manidx+mip, "<DL>\n");
  1732.         mip+=5;
  1733.     }
  1734.     }
  1735.     subs=level;
  1736.     scan_troff(item, 1, &c);
  1737.     sprintf(manidx+mip, "<DT><A HREF=\"#%s\">%s</A><DD>\n", label, c);
  1738.     if (c) free(c);
  1739.     while (manidx[mip]) mip++;
  1740. }
  1741.  
  1742. char *skip_till_newline(char *c)
  1743. {
  1744.     int lvl=0;
  1745.  
  1746.     while (*c && *c!='\n' || lvl>0) {
  1747.     if (*c=='\\') {
  1748.         c++;
  1749.         if (*c=='}') lvl--; else if (*c=='{') lvl++;
  1750.     }
  1751.     c++;
  1752.     }
  1753.     c++;
  1754.     if (lvl<0 && newline_for_fun) {
  1755.     newline_for_fun = newline_for_fun+lvl;
  1756.     if (newline_for_fun<0) newline_for_fun=0;
  1757.     }
  1758.     return c;
  1759. }
  1760.  
  1761. int ifelseval=0;
  1762.  
  1763. char *scan_request(char *c)
  1764. {
  1765.                   /* BSD Mandoc stuff */
  1766.     static int mandoc_synopsis=0; /* True if we are in the synopsis section */
  1767.     static int mandoc_command=0;  /* True if this is mandoc page */
  1768.     static int mandoc_bd_options; /* Only copes with non-nested Bd's */
  1769.  
  1770.     int i,j,mode=0;
  1771.     char *h;
  1772.     char *wordlist[20];
  1773.     int words;
  1774.     char *sl;
  1775.     STRDEF *owndef;
  1776.     while (*c==' ' || *c=='\t') c++;
  1777.     if (c[0]=='\n') return c+1;
  1778.     if (c[1]=='\n') j=1; else j=2;
  1779.     while (c[j]==' ' || c[j]=='\t') j++;
  1780.     if (c[0]==escapesym) {
  1781.     /* some pages use .\" .\$1 .\} */
  1782.     /* .\$1 is too difficult/stuppid */
  1783.     if (c[1]=='$') c=skip_till_newline(c);
  1784.     else
  1785.         c = scan_escape(c+1);
  1786.     } else {
  1787.     i=V(c[0],c[1]);
  1788.     switch (i) {
  1789.     case V('a','b'):
  1790.         h=c+j;
  1791.         while (*h && *h !='\n') h++;
  1792.         *h='\0';
  1793.         if (scaninbuff && buffpos) {
  1794.         buffer[buffpos]='\0';
  1795.         printf("%s\n", buffer);
  1796.         }
  1797.         fprintf(stderr, "%s\n", c+2);
  1798.         exit(0);
  1799.         break;
  1800.     case V('d','i'):
  1801.         {
  1802.         STRDEF *de;
  1803.         int oldcurpos=curpos;
  1804.         c=c+j;
  1805.         i=V(c[0],c[1]);
  1806.         if (*c=='\n') { c++;break; }
  1807.         while (*c && *c!='\n') c++;
  1808.         c++;
  1809.         h=c;
  1810.         while (*c && strncmp(c,".di",3)) while (*c && *c++!='\n');
  1811.         *c='\0';
  1812.         de=strdef;
  1813.         while (de && de->nr !=i) de=de->next;
  1814.         if (!de) {
  1815.             de=(STRDEF*) malloc(sizeof(STRDEF));
  1816.             de->nr=i;
  1817.             de->slen=0;
  1818.             de->next=strdef;
  1819.             de->st=NULL;
  1820.             strdef=de;
  1821.         } else {
  1822.             if (de->st) free(de->st);
  1823.             de->slen=0;
  1824.             de->st=NULL;
  1825.         }
  1826.         scan_troff(h,0,&de->st);
  1827.         *c='.';
  1828.         while (*c && *c++!='\n');
  1829.         break;
  1830.         }
  1831.     case V('d','s'):
  1832.         mode=1;
  1833.     case V('a','s'):
  1834.         {
  1835.         STRDEF *de;
  1836.         int oldcurpos=curpos;
  1837.         c=c+j;
  1838.         i=V(c[0],c[1]);
  1839.         j=0;
  1840.         while (c[j] && c[j]!='\n') j++;
  1841.         if (j<3) { c=c+j; break; }
  1842.         if (c[1]==' ') c=c+1; else c=c+2;
  1843.         while (isspace(*c)) c++;
  1844.         if (*c=='"') c++;
  1845.         de=strdef;
  1846.         while (de && de->nr != i) de=de->next;
  1847.         single_escape=1;
  1848.         curpos=0;
  1849.         if (!de) {
  1850.             char *h;
  1851.             de=(STRDEF*) malloc(sizeof(STRDEF));
  1852.             de->nr=i;
  1853.             de->slen=0;
  1854.             de->next=strdef;
  1855.             de->st=NULL;
  1856.             strdef=de;
  1857.             h=NULL;
  1858.             c=scan_troff(c, 1, &h);
  1859.             de->st=h;
  1860.             de->slen=curpos;
  1861.         } else {
  1862.             if (mode) {
  1863.             char *h=NULL;
  1864.             c=scan_troff(c, 1, &h);
  1865.             free(de->st);
  1866.             de->slen=0;
  1867.             de->st=h;
  1868.             } else
  1869.             c=scan_troff(c,1,&de->st);
  1870.             de->slen+=curpos;
  1871.         }
  1872.         single_escape=0;
  1873.         curpos=oldcurpos;
  1874.         }
  1875.         break;
  1876.     case V('b','r'):
  1877.         if (still_dd) out_html("<DD>");
  1878.         else out_html("<BR>\n");
  1879.         curpos=0;
  1880.         c=c+j;
  1881.         if (c[0]==escapesym) { c=scan_escape(c+1); }
  1882.         c=skip_till_newline(c);break;
  1883.     case V('c','2'):
  1884.         c=c+j;
  1885.         if (*c!='\n') { nobreaksym=*c; }
  1886.         else nobreaksym='\'';
  1887.         c=skip_till_newline(c);
  1888.         break;
  1889.     case V('c','c'):
  1890.         c=c+j;
  1891.         if (*c!='\n') { controlsym=*c; }
  1892.         else controlsym='.';
  1893.         c=skip_till_newline(c);
  1894.         break;
  1895.     case V('c','e'):
  1896.         c=c+j;
  1897.         if (*c=='\n') { i=1; }
  1898.         else {
  1899.         i=0;
  1900.         while ('0'<=*c && *c<='9') {
  1901.             i=i*10+*c-'0';
  1902.             c++;
  1903.         }
  1904.         }
  1905.         c=skip_till_newline(c);
  1906.         /* center next i lines */
  1907.         if (i>0) {
  1908.         out_html("<CENTER>\n");
  1909.         while (i && *c) {
  1910.             char *line=NULL;
  1911.             c=scan_troff(c,1, &line);
  1912.             if (line && strncmp(line, "<BR>", 4)) {
  1913.             out_html(line);
  1914.             out_html("<BR>\n");
  1915.             i--;
  1916.             }
  1917.         }
  1918.         out_html("</CENTER>\n");
  1919.         curpos=0;
  1920.         }
  1921.         break;
  1922.     case V('e','c'):
  1923.         c=c+j;
  1924.         if (*c!='\n') { escapesym=*c; }
  1925.         else escapesym='\\';
  1926.         break;
  1927.         c=skip_till_newline(c);
  1928.     case V('e','o'):
  1929.         escapesym='\0';
  1930.         c=skip_till_newline(c);
  1931.         break;
  1932.     case V('e','x'):
  1933.         exit(0);
  1934.         break;
  1935.     case V('f','c'):
  1936.         c=c+j;
  1937.         if  (*c=='\n') {
  1938.         fieldsym=padsym='\0';
  1939.         } else {
  1940.         fieldsym=c[0];
  1941.         padsym=c[1];
  1942.         }
  1943.         c=skip_till_newline(c);
  1944.         break;
  1945.     case V('f','i'):
  1946.         if (!fillout) {
  1947.         out_html(change_to_font(0));
  1948.         out_html(change_to_size('0'));
  1949.         out_html("</PRE>\n");
  1950.         }
  1951.         curpos=0;
  1952.         fillout=1;
  1953.         c=skip_till_newline(c);
  1954.         break;
  1955.     case V('f','t'):
  1956.         c=c+j;
  1957.         if (*c=='\n') {
  1958.         out_html(change_to_font(0));
  1959.         } else {
  1960.         if (*c==escapesym) {
  1961.             int fn;
  1962.             c=scan_expression(c, &fn);
  1963.             c--;
  1964.             out_html(change_to_font(fn));
  1965.         } else {
  1966.             out_html(change_to_font(*c));
  1967.             c++;
  1968.         }
  1969.         }
  1970.         c=skip_till_newline(c);
  1971.         break;
  1972.     case V('e','l'):
  1973.         /* .el anything : else part of if else */
  1974.         if (ifelseval) {
  1975.         c=c+j;
  1976.         c[-1]='\n';
  1977.         c=scan_troff(c,1,NULL);
  1978.         } else
  1979.         c=skip_till_newline(c+j);
  1980.         break;
  1981.     case V('i','e'):
  1982.         /* .ie c anything : then part of if else */    
  1983.     case V('i','f'):
  1984.         /* .if c anything
  1985.          * .if !c anything
  1986.          * .if N anything
  1987.          * .if !N anything
  1988.          * .if 'string1'string2' anything
  1989.          * .if !'string1'string2' anything
  1990.          */
  1991.         c=c+j;
  1992.         c=scan_expression(c, &i);
  1993.         ifelseval=!i;
  1994.         if (i) {
  1995.         *c='\n';
  1996.         c++;
  1997.         c=scan_troff(c,1,NULL);
  1998.         } else
  1999.         c=skip_till_newline(c);
  2000.         break;
  2001.     case V('i','g'):
  2002.         {
  2003.         char *endwith="..\n";
  2004.         i=3;
  2005.         c=c+j;
  2006.         if (*c!='\n') {
  2007.             endwith=c-1;i=1;
  2008.             c[-1]='.';
  2009.             while (*c!='\n') c++,i++;
  2010.         }
  2011.         c++;
  2012.         while (*c && strncmp(c,endwith,i)) while (*c++!='\n');
  2013.         while (*c++!='\n');
  2014.         break;
  2015.         }
  2016.     case V('n','f'):
  2017.         if (fillout) {
  2018.         out_html(change_to_font(0));
  2019.         out_html(change_to_size('0'));
  2020.         out_html("<PRE>\n");
  2021.         }
  2022.         curpos=0;
  2023.         fillout=0;
  2024.         c=skip_till_newline(c);
  2025.         break;
  2026.     case V('p','s'):
  2027.         c=c+j;
  2028.         if (*c=='\n') {
  2029.         out_html(change_to_size('0'));
  2030.         } else {
  2031.         j=0;i=0;
  2032.         if (*c=='-') { j= -1;c++; } else if (*c=='+') { j=1;c++;}
  2033.         c=scan_expression(c, &i);
  2034.         if (!j) { j=1; if (i>5) i=i-10; }
  2035.         out_html(change_to_size(i*j));
  2036.         }
  2037.         c=skip_till_newline(c);
  2038.         break;
  2039.     case V('s','p'):
  2040.         c=c+j;
  2041.         if (fillout) out_html("<P>"); else {
  2042.         out_html(NEWLINE);
  2043.         NEWLINE[0]='\n';
  2044.         }
  2045.         curpos=0;
  2046.         c=skip_till_newline(c);
  2047.         break;
  2048.     case V('s','o'):
  2049.         {
  2050.         FILE *f;
  2051.         struct stat stbuf;
  2052.         int l;char *buf;
  2053.         char *name=NULL;
  2054.         curpos=0;
  2055.         c=c+j;
  2056.         if (*c=='/') {
  2057.             h=c;
  2058.         } else {
  2059.             h=c-3;
  2060.             h[0]='.';
  2061.             h[1]='.';
  2062.             h[2]='/';
  2063.         }
  2064.         while (*c!='\n') c++;
  2065.         *c='\0';
  2066.         scan_troff(h,1, &name);
  2067.         if (name[3]=='/') h=name+3; else h=name;
  2068.         if (stat(h, &stbuf)!=-1) l=stbuf.st_size;
  2069.         buf = (char*) malloc((l+4)*sizeof(char));
  2070. #if NOCGI
  2071.                 if (!out_length) {
  2072.             char *t,*s;
  2073.             t=strrchr(fname, '/');
  2074.             if (!t) t=fname;
  2075.             fprintf(stderr, "ln -s %s.html %s.html\n", h, t);
  2076.             s=strrchr(t, '.');if (!s) s=t;
  2077.             printf("<HTML><HEAD><TITLE> Manpage of %s</TITLE>\n"
  2078.                "</HEAD><BODY>\n"
  2079.                "See the manpage for <A HREF=\"%s.html\">%s</A>.\n"
  2080.                "</BODY></HTML>\n",
  2081.                s, h, h);
  2082.         } else
  2083. #endif
  2084.                 {
  2085.             /* this works alright, except for section 3 */
  2086.             f=fopen(h,"r");
  2087.             if (!f || !buf || !l)
  2088.             fprintf(stderr, "Unable to open or read file %s.\n",
  2089.                 h); 
  2090.             else {
  2091.             i=fread(buf+1,1,l,f);
  2092.             fclose(f);
  2093.             buf[0]=buf[l]='\n';
  2094.             buf[l+1]=buf[l+2]='\0';
  2095.             scan_troff(buf+1,0,NULL);
  2096.             }
  2097.             if (buf) free(buf);
  2098.         }
  2099.         *c++='\n';
  2100.         break;
  2101.         }
  2102.     case V('t','a'):
  2103.         c=c+j;
  2104.         j=0;
  2105.         while (*c!='\n') {
  2106.         sl=scan_expression(c, &tabstops[j]);
  2107.         if (*c=='-' || *c=='+') tabstops[j]+=tabstops[j-1];
  2108.         c=sl;
  2109.         while (*c==' ' || *c=='\t') c++;
  2110.         j++;
  2111.         }
  2112.         maxtstop=j;
  2113.         curpos=0;
  2114.         break;
  2115.     case V('t','i'):
  2116.         /*while (itemdepth || dl_set[itemdepth]) {
  2117.         out_html("</DL>\n");
  2118.         if (dl_set[itemdepth]) dl_set[itemdepth]=0;
  2119.         else itemdepth--;
  2120.         }*/
  2121.         out_html("<BR>\n");
  2122.         c=c+j;
  2123.         c=scan_expression(c, &j);
  2124.         for (i=0; i<j; i++) out_html(" ");
  2125.         curpos=j;
  2126.         c=skip_till_newline(c);
  2127.         break;
  2128.     case V('t','m'):
  2129.         c=c+j;
  2130.         h=c;
  2131.         while (*c!='\n') c++;
  2132.         *c='\0';
  2133.         fprintf(stderr,"%s\n", h);
  2134.         *c='\n';
  2135.         break;
  2136.     case V('B',' '):
  2137.     case V('B','\n'):
  2138.     case V('I',' '):
  2139.     case V('I','\n'):
  2140.             /* parse one line in a certain font */
  2141.         out_html(change_to_font(*c));
  2142.         trans_char(c,'"','\a');
  2143.         c=c+j;
  2144.         if (*c=='\n') c++;
  2145.         c=scan_troff(c, 1, NULL);
  2146.         out_html(change_to_font('R'));
  2147.         out_html(NEWLINE);
  2148.         if (fillout) curpos++; else curpos=0;
  2149.         break;
  2150.     case V('O','P'):  /* groff manpages use this construction */
  2151.             /* .OP a b : [ <B>a</B> <I>b</I> ] */
  2152.         mode=1;
  2153.         c[0]='B'; c[1]='I';
  2154.         out_html(change_to_font('R'));
  2155.         out_html("[");
  2156.         curpos++;
  2157.     case V('B','R'):
  2158.     case V('B','I'):
  2159.     case V('I','B'):
  2160.     case V('I','R'):
  2161.     case V('R','B'):
  2162.     case V('R','I'):
  2163.         {
  2164.         char font[2] = { c[0], c[1] };
  2165.         c=c+j;
  2166.         if (*c=='\n') c++;
  2167.         sl=fill_words(c, wordlist, &words);
  2168.         c=sl+1;
  2169.         /* .BR name (section)
  2170.         ** indicates a link. It will be added in the output routine.
  2171.         */
  2172.         for (i=0; i<words; i++) {
  2173.             if (mode) { out_html(" "); curpos++; }
  2174.             wordlist[i][-1]=' ';
  2175.             out_html(change_to_font(font[i&1]));
  2176.             scan_troff(wordlist[i],1,NULL);
  2177.         }
  2178.         out_html(change_to_font('R'));
  2179.         if (mode) { out_html(" ]"); curpos++;}
  2180.         out_html(NEWLINE); if (!fillout) curpos=0; else curpos++;
  2181.         }
  2182.         break;
  2183.     case V('D','T'):
  2184.         for (j=0;j<20; j++) tabstops[j]=(j+1)*8;
  2185.         maxtstop=20;
  2186.         c=skip_till_newline(c); break;
  2187.     case V('I','P'):
  2188.         sl=fill_words(c+j, wordlist, &words);
  2189.         c=sl+1;
  2190.             if (!dl_set[itemdepth]) {
  2191.         out_html("<DL COMPACT>\n");
  2192.         dl_set[itemdepth]=1;
  2193.         }
  2194.         out_html("<DT>");
  2195.             if (words) {
  2196.         scan_troff(wordlist[0], 1,NULL);
  2197.         }
  2198.         out_html("<DD>");
  2199.         curpos=0;
  2200.         break;
  2201.     case V('T','P'):
  2202.         if (!dl_set[itemdepth]) {
  2203.         out_html("<DL COMPACT>\n");
  2204.         dl_set[itemdepth]=1;
  2205.         }
  2206.         out_html("<DT>");
  2207.         c=skip_till_newline(c);
  2208.         /* somewhere a definition ends with '.TP' */
  2209.         if (!*c) still_dd=1; else {
  2210.         c=scan_troff(c,1,NULL);
  2211.         out_html("<DD>");
  2212.         }
  2213.         curpos=0;
  2214.         break;
  2215.     case V('I','X'):
  2216.             /* general index */
  2217.         sl = fill_words(c+j, wordlist, &words);
  2218.         c=sl+1;
  2219.         j=4;
  2220.         while (idxlabel[j]=='Z') idxlabel[j--]='A';
  2221.         idxlabel[j]++;
  2222. #ifdef MAKEINDEX
  2223.             fprintf(idxfile, "%s@%s@", fname, idxlabel);
  2224.         for (j=0; j<words; j++) {
  2225.         h=NULL;
  2226.         scan_troff(wordlist[j], 1, &h);
  2227.         fprintf(idxfile, "_\b@%s", h);
  2228.         free(h);
  2229.         }
  2230.         fprintf(idxfile,"\n");
  2231. #endif
  2232.             out_html("<A NAME=\"");
  2233.         out_html(idxlabel);
  2234.         /* this will not work in mosaic (due to a bug).
  2235.         ** Adding ' ' between '>' and '<' solves it, but creates
  2236.         ** some space. A normal space does not work.
  2237.         */
  2238.         out_html("\"></A>");
  2239.         break;
  2240.     case V('L','P'):
  2241.     case V('P','P'):
  2242.         if (dl_set[itemdepth]) {
  2243.         out_html("</DL>\n");
  2244.         dl_set[itemdepth]=0;
  2245.         }
  2246.         if (fillout) out_html("<P>\n"); else {
  2247.         out_html(NEWLINE);
  2248.         NEWLINE[0]='\n';
  2249.         }
  2250.         curpos=0;
  2251.         c=skip_till_newline(c);
  2252.         break;
  2253.     case V('H','P'):
  2254.         if (!dl_set[itemdepth]) {
  2255.         out_html("<DL COMPACT>");
  2256.         dl_set[itemdepth]=1;
  2257.         }
  2258.         out_html("<DT>\n");
  2259.         still_dd=1;
  2260.         c=skip_till_newline(c);
  2261.         curpos=0;
  2262.         break;
  2263.     case V('P','D'): 
  2264.         c=skip_till_newline(c); 
  2265.         break;
  2266.     case V('R','s'):    /* BSD mandoc */
  2267.     case V('R','S'):
  2268.         sl=fill_words(c+j, wordlist, &words);
  2269.         j=1;
  2270.         if (words>0) scan_expression(wordlist[0], &j);
  2271.         if (j>=0) {
  2272.         itemdepth++;
  2273.         dl_set[itemdepth]=0;
  2274.         out_html("<DL COMPACT><DT><DD>");
  2275.         c=skip_till_newline(c);
  2276.         curpos=0;
  2277.         break;
  2278.         }
  2279.     case V('R','e'):    /* BSD mandoc */
  2280.     case V('R','E'):
  2281.         if (itemdepth > 0) {
  2282.         if (dl_set[itemdepth]) out_html("</DL>");
  2283.         out_html("</DL>\n");
  2284.         itemdepth--;
  2285.         }
  2286.         c=skip_till_newline(c);
  2287.         curpos=0;
  2288.         break;
  2289.     case V('S','B'):
  2290.         out_html(change_to_size(-1));
  2291.         out_html(change_to_font('B'));
  2292.         c=scan_troff(c+j, 1, NULL);
  2293.         out_html(change_to_font('R'));
  2294.         out_html(change_to_size('0'));
  2295.         break;
  2296.     case V('S','M'):
  2297.         c=c+j;
  2298.         if (*c=='\n') c++;
  2299.         out_html(change_to_size(-1));
  2300.         trans_char(c,'"','\a');
  2301.         c=scan_troff(c,1,NULL);
  2302.         out_html(change_to_size('0'));
  2303.         break;
  2304.     case V('S','s'):    /* BSD mandoc */
  2305.         mandoc_command = 1;
  2306.     case V('S','S'):
  2307.         mode=1;
  2308.     case V('S','h'):    /* BSD mandoc */
  2309.                 /* hack for fallthru from above */
  2310.         mandoc_command = !mode || mandoc_command; 
  2311.     case V('S','H'):
  2312.         c=c+j;
  2313.         if (*c=='\n') c++;
  2314.         while (itemdepth || dl_set[itemdepth]) {
  2315.         out_html("</DL>\n");
  2316.         if (dl_set[itemdepth]) dl_set[itemdepth]=0;
  2317.         else if (itemdepth > 0) itemdepth--;
  2318.         }
  2319.         out_html(change_to_font(0));
  2320.         out_html(change_to_size(0));
  2321.         if (!fillout) {
  2322.         fillout=1;
  2323.         out_html("</PRE>");
  2324.         }
  2325.         trans_char(c,'"', '\a');
  2326.         add_to_index(mode, c);
  2327.         out_html("<A NAME=\"");
  2328.         out_html(label);
  2329.         /*   for mosaic users */
  2330.         if (mode) out_html("\"> </A>\n<H3>");
  2331.         else out_html("\"> </A>\n<H2>");
  2332.         mandoc_synopsis = strncmp(c, "SYNOPSIS", 8) == 0;
  2333.         c = mandoc_command ? scan_troff_mandoc(c,1,NULL) : scan_troff(c,1,NULL);
  2334.         if (mode) out_html("</H3>\n");
  2335.         else out_html("</H2>\n");
  2336.         curpos=0;
  2337.         break;
  2338.     case V('T','S'):
  2339.         c=scan_table(c);
  2340.         break;
  2341.     case V('D','t'):    /* BSD mandoc */
  2342.         mandoc_command = 1;
  2343.     case V('T','H'):
  2344.         if (!output_possible) {
  2345.         sl = fill_words(c+j, wordlist, &words);
  2346.         if (words>1) {
  2347.             for (i=1; i<words; i++) wordlist[i][-1]='\0';
  2348.             *sl='\0';
  2349.             output_possible=1;
  2350.             out_html("<HTML><HEAD><TITLE>Manpage of ");
  2351.             out_html(wordlist[0]);
  2352.             out_html("</TITLE>\n</HEAD><BODY>\n<H1>");
  2353.             out_html(wordlist[0]);
  2354.             out_html("</H1>\nSection: ");
  2355.             if (words>4) out_html(wordlist[4]);
  2356.             else
  2357.             out_html(section_name(wordlist[1]));
  2358.             out_html(" (");
  2359.             out_html(wordlist[1]);
  2360.             if (words>2) {
  2361.             out_html(")<BR>Updated: ");
  2362.             scan_troff(wordlist[2], 1, NULL);
  2363.             } else out_html(")");
  2364.             out_html("<BR><A HREF=\"#index\">Index</A>\n");
  2365.             *sl='\n';
  2366.             printf("<BR><A HREF=\""
  2367.                CGIBASE
  2368.                "\">Return to Main Contents</A>\n");
  2369.             out_html("<HR>\n");
  2370.             if (mandoc_command) out_html("<BR>BSD mandoc<BR>");
  2371.         }
  2372.         c=sl+1;
  2373.         } else c=skip_till_newline(c);
  2374.         curpos=0;
  2375.         break;
  2376.     case V('T','X'):
  2377.         sl=fill_words(c+j, wordlist, &words);
  2378.         *sl='\0';
  2379.         out_html(change_to_font('I'));
  2380.         if (words>1) wordlist[1][-1]='\0';
  2381.         c=lookup_abbrev(wordlist[0]);
  2382.         curpos+=strlen(c);
  2383.         out_html(c);
  2384.         out_html(change_to_font('R'));
  2385.         if (words>1)
  2386.         out_html(wordlist[1]);
  2387.         *sl='\n';
  2388.         c=sl+1;
  2389.         break;
  2390.     case V('r','m'):
  2391.             /* .rm xx : Remove request, macro or string */
  2392.     case V('r','n'):
  2393.             /* .rn xx yy : Rename request, macro or string xx to yy */
  2394.         {
  2395.         STRDEF *de;
  2396.         c=c+j;
  2397.         i=V(c[0],c[1]);
  2398.         c=c+2;
  2399.         while (isspace(*c) && *c!='\n') c++;
  2400.         j=V(c[0],c[1]);
  2401.         while (*c && *c!='\n') c++;
  2402.         c++;
  2403.         de=strdef;
  2404.         while (de && de->nr!=j) de=de->next;
  2405.         if (de) {
  2406.             if (de->st) free(de->st);
  2407.             de->nr=0;
  2408.         }
  2409.         de=strdef;
  2410.         while (de && de->nr!=i) de=de->next;
  2411.         if (de) de->nr=j;
  2412.         break;
  2413.         }
  2414.     case V('n','x'):
  2415.             /* .nx filename : next file. */
  2416.     case V('i','n'):
  2417.             /* .in +-N : Indent */
  2418.         c=skip_till_newline(c);
  2419.         break;
  2420.     case V('n','r'):
  2421.             /* .nr R +-N M: define and set number register R by +-N;
  2422.         **  auto-increment by M
  2423.         */
  2424.         {
  2425.         INTDEF *intd;
  2426.         c=c+j;
  2427.         i=V(c[0],c[1]);
  2428.         c=c+2;
  2429.         intd=intdef;
  2430.         while (intd && intd->nr!=i) intd=intd->next;
  2431.         if (!intd) {
  2432.             intd = (INTDEF*) malloc(sizeof(INTDEF));
  2433.             intd->nr=i;
  2434.             intd->val=0;
  2435.             intd->incr=0;
  2436.             intd->next=intdef;
  2437.             intdef=intd;
  2438.         }
  2439.         while (*c==' ' || *c=='\t') c++;
  2440.         c=scan_expression(c,&intd->val);
  2441.         if (*c!='\n') {
  2442.             while (*c==' ' || *c=='\t') c++;
  2443.             c=scan_expression(c,&intd->incr);
  2444.         }
  2445.         c=skip_till_newline(c);
  2446.         break;
  2447.         }
  2448.     case V('a','m'):
  2449.             /* .am xx yy : append to a macro. */
  2450.             /* define or handle as .ig yy */
  2451.         mode=1;
  2452.     case V('d','e'):
  2453.             /* .de xx yy : define or redefine macro xx; end at .yy (..) */
  2454.             /* define or handle as .ig yy */
  2455.         {
  2456.         STRDEF *de;
  2457.         int olen=0;
  2458.         c=c+j;
  2459.         sl=fill_words(c, wordlist, &words);
  2460.         i=V(c[0],c[1]);j=2;
  2461.         if (words==1) wordlist[1]=".."; else {
  2462.             wordlist[1]--;
  2463.             wordlist[1][0]='.';
  2464.             j=3;
  2465.         }
  2466.         c=sl+1;
  2467.         sl=c;
  2468.         while (*c && strncmp(c,wordlist[1],j)) c=skip_till_newline(c);
  2469.         de=defdef;
  2470.         while (de && de->nr!= i) de=de->next;
  2471.         if (mode && de) olen=strlen(de->st);
  2472.         j=olen+c-sl;
  2473.         h= (char*) malloc((j*2+4)*sizeof(char));
  2474.         if (h) {
  2475.             for (j=0; j<olen; j++)
  2476.             h[j]=de->st[j];
  2477.             if (!j || h[j-1]!='\n')
  2478.             h[j++]='\n';
  2479.             while (sl!=c) {
  2480.             if (sl[0]=='\\' && sl[1]=='\\') {
  2481.                 h[j++]='\\'; sl++;
  2482.             } else
  2483.                 h[j++]=*sl;
  2484.             sl++;
  2485.             }
  2486.             h[j]='\0';
  2487.             if (de) {
  2488.             if (de->st) free(de->st);
  2489.             de->st=h;
  2490.             } else {
  2491.             de = (STRDEF*) malloc(sizeof(STRDEF));
  2492.             de->nr=i;
  2493.             de->next=defdef;
  2494.             de->st=h;
  2495.             defdef=de;
  2496.             }
  2497.         }
  2498.         }
  2499.         c=skip_till_newline(c);
  2500.         break;
  2501.     case V('B','l'):    /* BSD mandoc */
  2502.       {
  2503.         char list_options[500]; 
  2504.         char *nl = strchr(c,'\n');
  2505.         c=c+j;
  2506.         if (dl_set[itemdepth]) {  /* These things can nest. */
  2507.             itemdepth++;
  2508.         }
  2509.         if (nl) {          /* Parse list options */
  2510.             strncpy(list_options, c, nl - c);
  2511.             list_options[nl - c] = '\0';
  2512.         }
  2513.         if (strstr(list_options, "-bullet")) { /* HTML Unnumbered List */
  2514.             dl_set[itemdepth] = BL_BULLET_LIST;
  2515.             out_html("<UL>\n");
  2516.         }
  2517.         else if (strstr(list_options, "-enum")) { /* HTML Ordered List */
  2518.             dl_set[itemdepth] = BL_ENUM_LIST;
  2519.             out_html("<OL>\n");
  2520.         }
  2521.         else {          /* HTML Descriptive List */
  2522.             dl_set[itemdepth] = BL_DESC_LIST;
  2523.             out_html("<DL COMPACT>\n");
  2524.         }
  2525.         if (fillout) out_html("<P>\n"); else {
  2526.         out_html(NEWLINE);
  2527.         NEWLINE[0]='\n';
  2528.         }
  2529.         curpos=0;
  2530.         c=skip_till_newline(c);
  2531.         break;
  2532.       }
  2533.     case V('E','l'):    /* BSD mandoc */
  2534.         c=c+j;
  2535.         if (dl_set[itemdepth] & BL_DESC_LIST) {
  2536.         out_html("</DL>\n");
  2537.         }
  2538.         else if (dl_set[itemdepth] & BL_BULLET_LIST) {
  2539.         out_html("</UL>\n");
  2540.         }
  2541.         else if (dl_set[itemdepth] & BL_ENUM_LIST) {
  2542.         out_html("</OL>\n");
  2543.         }
  2544.         dl_set[itemdepth]=0;
  2545.         if (itemdepth > 0) itemdepth--;
  2546.         if (fillout) out_html("<P>\n"); else {
  2547.         out_html(NEWLINE);
  2548.         NEWLINE[0]='\n';
  2549.         }
  2550.         curpos=0;
  2551.         c=skip_till_newline(c);
  2552.         break;
  2553.     case V('I','t'):    /* BSD mandoc */
  2554.         c=c+j;
  2555.         if (strncmp(c, "Xo", 2) == 0 && isspace(*(c+2))) {
  2556.             c = skip_till_newline(c); 
  2557.         }
  2558.         if (dl_set[itemdepth] & BL_DESC_LIST) {
  2559.             out_html("<DT>");
  2560.         out_html(change_to_font('B'));
  2561.             if (*c=='\n') {      /* Don't allow embedded comms after a newline */
  2562.             c++;      
  2563.             c=scan_troff(c,1,NULL);
  2564.         }
  2565.         else {          /* Do allow embedded comms on the same line. */
  2566.             c=scan_troff_mandoc(c,1,NULL);
  2567.         }
  2568.         out_html(change_to_font('R'));
  2569.         out_html(NEWLINE);
  2570.         out_html("<DD>");
  2571.         }
  2572.         else if (dl_set[itemdepth] & (BL_BULLET_LIST | BL_ENUM_LIST)) {
  2573.             out_html("<LI>");
  2574.         c=scan_troff_mandoc(c,1,NULL);
  2575.         out_html(NEWLINE);
  2576.         }
  2577.         if (fillout) curpos++; else curpos=0;
  2578.         break;
  2579.     case V('B','k'):    /* BSD mandoc */
  2580.     case V('E','k'):    /* BSD mandoc */
  2581.     case V('D','d'):    /* BSD mandoc */
  2582.     case V('O','s'):    /* BSD mandoc */
  2583.         trans_char(c,'"','\a');
  2584.         c=c+j;
  2585.         if (*c=='\n') c++;
  2586.         c=scan_troff_mandoc(c, 1, NULL);
  2587.         out_html(NEWLINE);
  2588.         if (fillout) curpos++; else curpos=0;
  2589.         break;
  2590.     case V('B','t'):    /* BSD mandoc */
  2591.         trans_char(c,'"','\a');
  2592.         c=c+j;
  2593.         out_html(" is currently in beta test.");
  2594.         if (fillout) curpos++; else curpos=0;
  2595.         break;
  2596.     case V('B','x'):    /* BSD mandoc */
  2597.         trans_char(c,'"','\a');
  2598.         c=c+j;
  2599.         if (*c=='\n') c++;
  2600.         out_html("BSD ");
  2601.         c=scan_troff_mandoc(c, 1, NULL);
  2602.         if (fillout) curpos++; else curpos=0;
  2603.         break;
  2604.     case V('D','l'):    /* BSD mandoc */
  2605.         c=c+j;
  2606.         out_html(NEWLINE);
  2607.         out_html("<BLOCKQUOTE>");        
  2608.         out_html(change_to_font('L'));
  2609.         if (*c=='\n') c++;
  2610.         c=scan_troff_mandoc(c, 1, NULL);        
  2611.         out_html(change_to_font('R'));
  2612.         out_html("</BLOCKQUOTE>");        
  2613.         if (fillout) curpos++; else curpos=0;
  2614.         break;
  2615.     case V('B','d'):    /* BSD mandoc */
  2616.       {            /* Seems like a kind of example/literal mode */
  2617.         char bd_options[500]; 
  2618.         char *nl = strchr(c,'\n');
  2619.         c=c+j;
  2620.         if (nl) {
  2621.           strncpy(bd_options, c, nl - c);
  2622.           bd_options[nl - c] = '\0';
  2623.         }
  2624.         out_html(NEWLINE);
  2625.         mandoc_bd_options = 0; /* Remember options for terminating Bl */
  2626.         if (strstr(bd_options, "-offset indent")) {
  2627.             mandoc_bd_options |= BD_INDENT;
  2628.             out_html("<BLOCKQUOTE>\n");
  2629.         }
  2630.         if (   strstr(bd_options, "-literal")
  2631.         || strstr(bd_options, "-unfilled")) {
  2632.             if (fillout) {
  2633.             mandoc_bd_options |= BD_LITERAL;
  2634.             out_html(change_to_font(0));
  2635.             out_html(change_to_size('0'));
  2636.             out_html("<PRE>\n");
  2637.         }
  2638.         curpos=0;
  2639.         fillout=0;
  2640.         }
  2641.         c=skip_till_newline(c);
  2642.         break;
  2643.       }
  2644.     case V('E','d'):    /* BSD mandoc */
  2645.         if (mandoc_bd_options & BD_LITERAL) {
  2646.             if (!fillout) {
  2647.             out_html(change_to_font(0));
  2648.             out_html(change_to_size('0'));
  2649.             out_html("</PRE>\n");
  2650.         }
  2651.         }
  2652.         if (mandoc_bd_options & BD_INDENT)
  2653.             out_html("</BLOCKQUOTE>\n");
  2654.         curpos=0;
  2655.         fillout=1;
  2656.         c=skip_till_newline(c);
  2657.         break;
  2658.     case V('B','e'):    /* BSD mandoc */
  2659.         c=c+j;
  2660.         if (fillout) out_html("<P>"); else {
  2661.         out_html(NEWLINE);
  2662.         NEWLINE[0]='\n';
  2663.         }
  2664.         curpos=0;
  2665.         c=skip_till_newline(c);
  2666.         break;
  2667.     case V('X','r'):    /* BSD mandoc */
  2668.         {
  2669.           /* Translate xyz 1 to xyz(1) 
  2670.            * Allow for multiple spaces.  Allow the section to be missing.
  2671.            */
  2672.           char buff[500];
  2673.           char *bufptr;
  2674.           trans_char(c,'"','\a');
  2675.           bufptr = buff;
  2676.           c = c+j;
  2677.           if (*c == '\n') c++; /* Skip spaces */
  2678.           while (isspace(*c) && *c != '\n') c++;
  2679.           while (isalnum(*c)) { /* Copy the xyz part */
  2680.         *bufptr = *c;
  2681.         bufptr++;
  2682.         c++;
  2683.           }
  2684.           while (isspace(*c) && *c != '\n') c++;    /* Skip spaces */
  2685.           if (isdigit(*c)) { /* Convert the number if there is one */
  2686.         *bufptr = '(';
  2687.         bufptr++; 
  2688.         while (isalnum(*c)) { 
  2689.           *bufptr = *c; 
  2690.           bufptr++;
  2691.           c++;
  2692.         }
  2693.         *bufptr = ')';
  2694.         bufptr++;
  2695.           }
  2696.  
  2697.           while (*c != '\n') { /* Copy the remainder */
  2698.         if (!isspace(*c)) {
  2699.           *bufptr = *c;
  2700.           bufptr++;
  2701.         }
  2702.         c++;
  2703.           }
  2704.           *bufptr = '\n';
  2705.           scan_troff_mandoc(buff, 1, NULL);
  2706.  
  2707.           out_html(NEWLINE);
  2708.           if (fillout) curpos++; else curpos=0;
  2709.         }
  2710.         break;      
  2711.     case V('F','l'):    /* BSD mandoc */
  2712.         trans_char(c,'"','\a');
  2713.         c=c+j;
  2714.         out_html("-");
  2715.         if (*c!='\n') {
  2716.             out_html(change_to_font('B'));
  2717.             c=scan_troff_mandoc(c, 1, NULL);
  2718.             out_html(change_to_font('R'));
  2719.             }
  2720.         out_html(NEWLINE);
  2721.         if (fillout) curpos++; else curpos=0;
  2722.         break;
  2723.     case V('P','a'):    /* BSD mandoc */
  2724.     case V('P','f'):    /* BSD mandoc */
  2725.         trans_char(c,'"','\a');
  2726.         c=c+j;
  2727.         if (*c=='\n') c++;
  2728.         c=scan_troff_mandoc(c, 1, NULL);
  2729.         out_html(NEWLINE);
  2730.         if (fillout) curpos++; else curpos=0;
  2731.         break;
  2732.     case V('P','p'):    /* BSD mandoc */
  2733.         if (fillout) out_html("<P>\n"); else {
  2734.         out_html(NEWLINE);
  2735.         NEWLINE[0]='\n';
  2736.         }
  2737.         curpos=0;
  2738.         c=skip_till_newline(c);
  2739.         break;
  2740.     case V('D','q'):    /* BSD mandoc */
  2741.         trans_char(c,'"','\a');
  2742.         c=c+j;
  2743.         if (*c=='\n') c++;
  2744.         out_html("``");
  2745.         c=scan_troff_mandoc(c, 1, NULL);
  2746.         out_html("''");
  2747.         out_html(NEWLINE);
  2748.         if (fillout) curpos++; else curpos=0;
  2749.         break;
  2750.     case V('O','p'):    /* BSD mandoc */
  2751.         trans_char(c,'"','\a');
  2752.         c=c+j;
  2753.         if (*c=='\n') c++;
  2754.         out_html(change_to_font('R'));
  2755.         out_html("[");
  2756.         c=scan_troff_mandoc(c, 1, NULL);
  2757.         out_html(change_to_font('R'));
  2758.         out_html("]");
  2759.         out_html(NEWLINE);
  2760.         if (fillout) curpos++; else curpos=0;
  2761.         break;
  2762.     case V('O','o'):    /* BSD mandoc */
  2763.         trans_char(c,'"','\a');
  2764.         c=c+j;
  2765.         if (*c=='\n') c++;
  2766.         out_html(change_to_font('R'));
  2767.         out_html("[");
  2768.         c=scan_troff_mandoc(c, 1, NULL);
  2769.         if (fillout) curpos++; else curpos=0;
  2770.         break;
  2771.     case V('O','c'):    /* BSD mandoc */
  2772.         trans_char(c,'"','\a');
  2773.         c=c+j;
  2774.         c=scan_troff_mandoc(c, 1, NULL);
  2775.         out_html(change_to_font('R'));
  2776.         out_html("]");
  2777.         if (fillout) curpos++; else curpos=0;
  2778.         break;
  2779.     case V('P','q'):    /* BSD mandoc */
  2780.         trans_char(c,'"','\a');
  2781.         c=c+j;
  2782.         if (*c=='\n') c++;
  2783.         out_html("(");
  2784.         c=scan_troff_mandoc(c, 1, NULL);
  2785.         out_html(")");
  2786.         out_html(NEWLINE);
  2787.         if (fillout) curpos++; else curpos=0;
  2788.         break;
  2789.     case V('Q','l'):    /* BSD mandoc */
  2790.       {            /* Single quote first word in the line */
  2791.         char *sp;
  2792.         trans_char(c,'"','\a');
  2793.         c=c+j;
  2794.         if (*c=='\n') c++;
  2795.         sp = c;      
  2796.         do {        /* Find first whitespace after the 
  2797.                  * first word that isn't a mandoc macro 
  2798.                  */
  2799.           while (*sp && isspace(*sp)) sp++;        
  2800.           while (*sp && !isspace(*sp)) sp++;
  2801.         } while (*sp && isupper(*(sp-2)) && islower(*(sp-1)));
  2802.  
  2803.                 /* Use a newline to mark the end of text to 
  2804.                  * be quoted 
  2805.                  */
  2806.         if (*sp) *sp = '\n';
  2807.         out_html("`");    /* Quote the text */
  2808.         c=scan_troff_mandoc(c, 1, NULL);
  2809.         out_html("'");
  2810.         out_html(NEWLINE);
  2811.         if (fillout) curpos++; else curpos=0;
  2812.         break;
  2813.       }
  2814.     case V('S','q'):    /* BSD mandoc */
  2815.         trans_char(c,'"','\a');
  2816.         c=c+j;
  2817.         if (*c=='\n') c++;
  2818.         out_html("`");
  2819.         c=scan_troff_mandoc(c, 1, NULL);
  2820.         out_html("'");
  2821.         out_html(NEWLINE);
  2822.         if (fillout) curpos++; else curpos=0;
  2823.         break;
  2824.     case V('A','r'):    /* BSD mandoc */
  2825.             /* parse one line in italics */
  2826.         out_html(change_to_font('I'));
  2827.         trans_char(c,'"','\a');
  2828.         c=c+j;
  2829.         if (*c=='\n') {    /* An empty Ar means "file ..." */
  2830.             out_html("file ...");
  2831.         }
  2832.         else {
  2833.             c=scan_troff_mandoc(c, 1, NULL);
  2834.         }
  2835.         out_html(change_to_font('R'));
  2836.         out_html(NEWLINE);
  2837.         if (fillout) curpos++; else curpos=0;
  2838.         break;
  2839.     case V('A','d'):    /* BSD mandoc */
  2840.     case V('E','m'):    /* BSD mandoc */
  2841.     case V('V','a'):    /* BSD mandoc */
  2842.     case V('X','c'):    /* BSD mandoc */
  2843.             /* parse one line in italics */
  2844.         out_html(change_to_font('I'));
  2845.         trans_char(c,'"','\a');
  2846.         c=c+j;
  2847.         if (*c=='\n') c++;
  2848.         c=scan_troff_mandoc(c, 1, NULL);
  2849.         out_html(change_to_font('R'));
  2850.         out_html(NEWLINE);
  2851.         if (fillout) curpos++; else curpos=0;
  2852.         break;
  2853.     case V('N','d'):    /* BSD mandoc */
  2854.         trans_char(c,'"','\a');
  2855.         c=c+j;
  2856.         if (*c=='\n') c++;
  2857.         out_html(" - ");
  2858.         c=scan_troff_mandoc(c, 1, NULL);
  2859.         out_html(NEWLINE);
  2860.         if (fillout) curpos++; else curpos=0;
  2861.         break;
  2862.     case V('N','m'):    /* BSD mandoc */
  2863.       {
  2864.         static char mandoc_name[100] = "";
  2865.         trans_char(c,'"','\a');
  2866.         c=c+j;
  2867.         if (mandoc_synopsis) {    /* Break lines only in the Synopsis. 
  2868.                        * The Synopsis section seems to be treated
  2869.                        * as a special case - Bummer!
  2870.                        */
  2871.             static int count = 0; /* Don't break on the first Nm */
  2872.             if (count) {
  2873.             out_html("<BR>");
  2874.         }
  2875.         else {
  2876.             char *end = strchr(c, '\n');
  2877.             if (end) {    /* Remember the name for later. */
  2878.                 strncpy(mandoc_name, c, end - c);
  2879.             }
  2880.         }
  2881.         count++;
  2882.         }            
  2883.         out_html(change_to_font('B'));
  2884.         while (*c == ' '|| *c == '\t') c++;
  2885.         if (*c == '\n') {    /* If Nm has no argument, use one from an earlier
  2886.                  * Nm command that did have one.  Hope there aren't
  2887.                  * too many commands that do this.
  2888.                  */
  2889.             out_html(mandoc_name);
  2890.         } 
  2891.         else {
  2892.             c=scan_troff_mandoc(c, 1, NULL);
  2893.         }
  2894.         out_html(change_to_font('R'));
  2895.         out_html(NEWLINE);
  2896.         if (fillout) curpos++; else curpos=0;
  2897.         break;
  2898.       }
  2899.     case V('C','d'):    /* BSD mandoc */
  2900.     case V('C','m'):    /* BSD mandoc */
  2901.     case V('I','c'):    /* BSD mandoc */
  2902.     case V('M','s'):    /* BSD mandoc */
  2903.     case V('O','r'):    /* BSD mandoc */
  2904.     case V('S','y'):    /* BSD mandoc */
  2905.             /* parse one line in bold */
  2906.         out_html(change_to_font('B'));
  2907.         trans_char(c,'"','\a');
  2908.         c=c+j;
  2909.         if (*c=='\n') c++;
  2910.         c=scan_troff_mandoc(c, 1, NULL);
  2911.         out_html(change_to_font('R'));
  2912.         out_html(NEWLINE);
  2913.         if (fillout) curpos++; else curpos=0;
  2914.         break;
  2915.     case V('D','v'):    /* BSD mandoc */
  2916.     case V('E','v'):    /* BSD mandoc */
  2917.     case V('F','r'):    /* BSD mandoc */
  2918.     case V('L','i'):    /* BSD mandoc */
  2919.     case V('N','o'):    /* BSD mandoc */
  2920.     case V('N','s'):    /* BSD mandoc */
  2921.     case V('T','n'):    /* BSD mandoc */
  2922.     case V('n','N'):    /* BSD mandoc */
  2923.         trans_char(c,'"','\a');
  2924.         c=c+j;
  2925.         if (*c=='\n') c++;
  2926.         out_html(change_to_font('B'));
  2927.         c=scan_troff_mandoc(c, 1, NULL);
  2928.         out_html(change_to_font('R'));
  2929.         out_html(NEWLINE);
  2930.         if (fillout) curpos++; else curpos=0;
  2931.         break;
  2932.     case V('%','A'):    /* BSD mandoc biblio stuff */
  2933.     case V('%','D'):
  2934.     case V('%','N'):
  2935.     case V('%','O'):
  2936.     case V('%','P'):
  2937.     case V('%','Q'):
  2938.     case V('%','V'):
  2939.         c=c+j;
  2940.         if (*c=='\n') c++;
  2941.         c=scan_troff(c, 1, NULL); /* Don't allow embedded mandoc coms */
  2942.         if (fillout) curpos++; else curpos=0;
  2943.         break;
  2944.     case V('%','B'):
  2945.     case V('%','J'):
  2946.     case V('%','R'):
  2947.     case V('%','T'):
  2948.         c=c+j;
  2949.         out_html(change_to_font('I'));
  2950.         if (*c=='\n') c++;
  2951.         c=scan_troff(c, 1, NULL); /* Don't allow embedded mandoc coms */
  2952.         out_html(change_to_font('R'));
  2953.         if (fillout) curpos++; else curpos=0;
  2954.         break;
  2955.     default:
  2956.             /* search macro database of self-defined macros */
  2957.         owndef = defdef;
  2958.         while (owndef && owndef->nr!=i) owndef=owndef->next;
  2959.         if (owndef) {
  2960.         char **oldargument;
  2961.         int deflen;
  2962.         int onff;
  2963.         sl=fill_words(c+j, wordlist, &words);
  2964.         c=sl+1;
  2965.         *sl='\0';
  2966.         for (i=1;i<words; i++) wordlist[i][-1]='\0';
  2967.         for (i=0; i<words; i++) {
  2968.             char *h=NULL;
  2969.             if (mandoc_command) {
  2970.               scan_troff_mandoc(wordlist[i],1,&h);
  2971.             }
  2972.             else {
  2973.               scan_troff(wordlist[i],1,&h);
  2974.             }
  2975.             wordlist[i]=h;
  2976.         }
  2977.         for (i=words;i<20; i++) wordlist[i]=NULL;
  2978.         deflen = strlen(owndef->st);
  2979.         for (i=0; owndef->st[deflen+2+i]=owndef->st[i]; i++);
  2980.         oldargument=argument;
  2981.         argument=wordlist;
  2982.         onff=newline_for_fun;
  2983.         if (mandoc_command) {
  2984.           scan_troff_mandoc(owndef->st+deflen+2, 0, NULL);
  2985.         }
  2986.         else {
  2987.           scan_troff(owndef->st+deflen+2, 0, NULL);
  2988.         }
  2989.         newline_for_fun=onff;
  2990.         argument=oldargument;
  2991.         for (i=0; i<words; i++) if (wordlist[i]) free(wordlist[i]);
  2992.         *sl='\n';
  2993.         }
  2994.         else if (mandoc_command && 
  2995.              ((isupper(*c) && islower(*(c+1)))
  2996.               || (islower(*c) && isupper(*(c+1))))
  2997.              ) {    /* Let through any BSD mandoc commands that haven't
  2998.                  * been delt with.
  2999.                  * I don't want to miss anything out of the text.
  3000.                  */
  3001.             char buf[4];
  3002.         strncpy(buf,c,2);
  3003.         buf[2] = ' ';    
  3004.         buf[3] = '\0';
  3005.         out_html(buf);    /* Print the command (it might just be text). */
  3006.             c=c+j;
  3007.             trans_char(c,'"','\a');
  3008.         if (*c=='\n') c++;
  3009.         out_html(change_to_font('R'));
  3010.         c=scan_troff(c, 1, NULL);
  3011.         out_html(NEWLINE);
  3012.         if (fillout) curpos++; else curpos=0;
  3013.         }
  3014.         else {
  3015.         c=skip_till_newline(c);
  3016.         }
  3017.         break;
  3018.     }
  3019.     }
  3020.     if (fillout) { out_html(NEWLINE); curpos++; }
  3021.     NEWLINE[0]='\n';
  3022.     return c;
  3023. }
  3024.  
  3025. void flush(void)
  3026. {
  3027. }
  3028.  
  3029. static int contained_tab=0;
  3030. static int mandoc_line=0;    /* Signals whether to look for embedded mandoc
  3031.                  * commands.
  3032.                  */
  3033.  
  3034. char *scan_troff(char *c, int san, char **result)
  3035. {   /* san : stop at newline */
  3036.     char *h;
  3037.     char intbuff[500];
  3038.     int ibp=0;
  3039.     int i;
  3040. #define FLUSHIBP  if (ibp) { intbuff[ibp]=0; out_html(intbuff); ibp=0; }
  3041.     char *exbuffer;
  3042.     int exbuffpos, exbuffmax, exscaninbuff, exnewline_for_fun;
  3043.     int usenbsp=0;
  3044.  
  3045.     exbuffer=buffer;
  3046.     exbuffpos=buffpos;
  3047.     exbuffmax=buffmax;
  3048.     exnewline_for_fun=newline_for_fun;
  3049.     exscaninbuff=scaninbuff;
  3050.     newline_for_fun=0;
  3051.     if (result) {
  3052.     if (*result) {
  3053.         buffer=*result;
  3054.         buffpos=strlen(buffer);
  3055.         buffmax=buffpos;
  3056.     } else {
  3057.         buffer=(char *) malloc(1000*sizeof(char));
  3058.         buffpos=0;
  3059.         buffmax=1000;
  3060.     }
  3061.     scaninbuff=1;
  3062.     }
  3063.     h=c;
  3064.     /* start scanning */
  3065.  
  3066.     while (*h && (!san || newline_for_fun || *h!='\n')) {
  3067.  
  3068.     if (*h==escapesym) {
  3069.         h++;
  3070.         FLUSHIBP;
  3071.         h = scan_escape(h);
  3072.     } else if (*h==controlsym && h[-1]=='\n') {
  3073.         h++;
  3074.         FLUSHIBP;
  3075.         h = scan_request(h);
  3076.         if (san && h[-1]=='\n') h--;
  3077.     } else if (mandoc_line
  3078.            && *(h) && isupper(*(h))
  3079.            && *(h+1) && islower(*(h+1)) 
  3080.            && *(h+2) && isspace(*(h+2))) {
  3081.         /* BSD imbedded command eg ".It Fl Ar arg1 Fl Ar arg2" */
  3082.         FLUSHIBP;
  3083.         h = scan_request(h);
  3084.         if (san && h[-1]=='\n') h--;
  3085.     } else if (*h==nobreaksym && h[-1]=='\n') {
  3086.         h++;
  3087.         FLUSHIBP;
  3088.         h = scan_request(h);
  3089.         if (san && h[-1]=='\n') h--;
  3090.     } else {
  3091.         int mx;
  3092.         if (h[-1]=='\n' && still_dd && isalnum(*h)) {
  3093.         /* sometimes a .HP request is not followed by a .br request */
  3094.         FLUSHIBP;
  3095.         out_html("<DD>");
  3096.         curpos=0;
  3097.         still_dd=0;
  3098.         }
  3099.         switch (*h) {
  3100.         case '&':
  3101.         intbuff[ibp++]='&';
  3102.         intbuff[ibp++]='a';
  3103.         intbuff[ibp++]='m';
  3104.         intbuff[ibp++]='p';
  3105.         intbuff[ibp++]=';';
  3106.         curpos++;
  3107.         break;
  3108.         case '<':
  3109.         intbuff[ibp++]='&';
  3110.         intbuff[ibp++]='l';
  3111.         intbuff[ibp++]='t';
  3112.         intbuff[ibp++]=';';
  3113.         curpos++;
  3114.         break;
  3115.         case '>':
  3116.         intbuff[ibp++]='&';
  3117.         intbuff[ibp++]='g';
  3118.         intbuff[ibp++]='t';
  3119.         intbuff[ibp++]=';';
  3120.         curpos++;
  3121.         break;
  3122.         case '"':
  3123.         intbuff[ibp++]='&';
  3124.         intbuff[ibp++]='q';
  3125.         intbuff[ibp++]='u';
  3126.         intbuff[ibp++]='o';
  3127.         intbuff[ibp++]='t';
  3128.         intbuff[ibp++]=';';
  3129.         curpos++;
  3130.         break;
  3131.         case '\n':
  3132.         if (h[-1]=='\n' && fillout) {
  3133.             intbuff[ibp++]='<';
  3134.             intbuff[ibp++]='P';
  3135.             intbuff[ibp++]='>';
  3136.         }
  3137.         if (contained_tab && fillout) {
  3138.             intbuff[ibp++]='<';
  3139.             intbuff[ibp++]='B';
  3140.             intbuff[ibp++]='R';
  3141.             intbuff[ibp++]='>';
  3142.         }
  3143.         contained_tab=0;
  3144.         curpos=0;
  3145.         usenbsp=0;
  3146.         intbuff[ibp++]='\n';
  3147.         break;
  3148.         case '\t':
  3149.         {
  3150.             int curtab=0;
  3151.             contained_tab=1;
  3152.             FLUSHIBP;
  3153.             /* like a typewriter, not like TeX */
  3154.             tabstops[19]=curpos+1;
  3155.             while (curtab<maxtstop && tabstops[curtab]<=curpos)
  3156.             curtab++;
  3157.             if (curtab<maxtstop) {
  3158.             if (!fillout) {
  3159.                 while (curpos<tabstops[curtab]) {
  3160.                 intbuff[ibp++]=' ';
  3161.                 if (ibp>480) { FLUSHIBP; }
  3162.                 curpos++;
  3163.                 }
  3164.             } else {
  3165.                 out_html("<TT>");
  3166.                 while (curpos<tabstops[curtab]) {
  3167.                 out_html(" ");
  3168.                 curpos++;
  3169.                 }
  3170.                 out_html("</TT>");
  3171.             }
  3172.             }
  3173.         }
  3174.         break;
  3175.         default:
  3176.         if (*h==' ' && (h[-1]=='\n' || usenbsp)) {
  3177.             FLUSHIBP;
  3178.             if (!usenbsp && fillout) {
  3179.             out_html("<BR>");
  3180.             curpos=0;
  3181.             }
  3182.             usenbsp=fillout;
  3183.             if (usenbsp) out_html(" "); else intbuff[ibp++]=' ';
  3184.         } else if (*h>31 && *h<127) intbuff[ibp++]=*h;
  3185.         else if (((unsigned char)(*h))>127) {
  3186.             intbuff[ibp++]='&';
  3187.             intbuff[ibp++]='#';
  3188.             intbuff[ibp++]='0'+((unsigned char)(*h))/100;
  3189.             intbuff[ibp++]='0'+(((unsigned char)(*h))%100)/10;
  3190.             intbuff[ibp++]='0'+((unsigned char)(*h))%10;
  3191.             intbuff[ibp++]=';';
  3192.         }
  3193.         curpos++;
  3194.         break;
  3195.         }
  3196.         if (ibp>480) FLUSHIBP;
  3197.         h++;
  3198.     }
  3199.     }
  3200.     FLUSHIBP;
  3201.     if (buffer) buffer[buffpos]='\0';
  3202.     if (san && *h) h++;
  3203.     newline_for_fun=exnewline_for_fun;
  3204.     if (result) {
  3205.     *result = buffer;
  3206.     buffer=exbuffer;
  3207.     buffpos=exbuffpos;
  3208.     buffmax=exbuffmax;
  3209.     scaninbuff=exscaninbuff;
  3210.     }
  3211.     return h;
  3212. }
  3213.  
  3214.  
  3215. char *scan_troff_mandoc(char *c, int san, char **result)
  3216. {
  3217.     char *ret, *end = c;
  3218.     int oldval = mandoc_line;
  3219.     mandoc_line = 1;
  3220.     while (*end && *end != '\n') {
  3221.         end++;
  3222.     }
  3223.  
  3224.     if (end > c + 2
  3225.         && ispunct(*(end - 1)) 
  3226.     && isspace(*(end - 2)) && *(end - 2) != '\n') {
  3227.       /* Don't format lonely punctuation E.g. in "xyz ," format
  3228.        * the xyz and then append the comma removing the space.
  3229.        */
  3230.         *(end - 2) = '\n';
  3231.     ret = scan_troff(c, san, result);
  3232.         *(end - 2) = *(end - 1);
  3233.         *(end - 1) = ' ';
  3234.     }
  3235.     else {
  3236.     ret = scan_troff(c, san, result);
  3237.     }
  3238.     mandoc_line = oldval;
  3239.     return ret;
  3240. }
  3241.  
  3242.  
  3243. char *sectionname=NULL;
  3244. STRDEF *foundpages=NULL;
  3245.  
  3246. int search_manpath_all(char *name)
  3247. {
  3248.     char smfbuf[1000];
  3249.     char cmpbuf[100];
  3250.     int i,j,n,l,nr=0;
  3251.     DIR *dr;
  3252.     struct dirent *de;
  3253.     STRDEF *h=NULL;
  3254.  
  3255.     strcpy(cmpbuf,name);
  3256.     n=strlen(name);
  3257.     cmpbuf[n++]='.';
  3258.     cmpbuf[n+1]='\0';
  3259.     for (i=0; manpath[i]; i++) {
  3260.     strcpy(smfbuf, manpath[i]);
  3261.     l=strlen(smfbuf);
  3262.     strcpy(smfbuf+l, "man");
  3263.     l+=3;
  3264.     smfbuf[l+1]='\0';
  3265.     for (j=0; sections[j]; j++) {
  3266.         smfbuf[l]=sections[j];
  3267.         cmpbuf[n]=sections[j];
  3268.         if ((dr=opendir(smfbuf))) {
  3269.         while ((de=readdir(dr))) {
  3270.             if (!strncasecmp(de->d_name, cmpbuf, n+1)) {
  3271.             int stlen;
  3272.             if (h) {
  3273.                 h->next=(STRDEF*) malloc(sizeof(STRDEF));
  3274.                 h=h->next;
  3275.             } else
  3276.                 h=foundpages=(STRDEF*) malloc(sizeof(STRDEF));
  3277.             h->nr=i*256+j;
  3278.             stlen=strlen(de->d_name)+1;
  3279.             h->st=(char*) malloc(stlen*sizeof(char));
  3280.             nr++;
  3281.             strcpy(h->st, de->d_name);
  3282.             h->next=NULL;
  3283.             }
  3284.         }
  3285.         closedir(dr);
  3286.         }
  3287.     }
  3288.     }
  3289.     return nr;
  3290. }
  3291.  
  3292. int search_manpath_section(char *name, char* section)
  3293. {
  3294.     char smfbuf[1000];
  3295.     char cmpbuf[100];
  3296.     int i,j,n,l,nr=0;
  3297.     DIR *dr;
  3298.     struct dirent *de;
  3299.     STRDEF *h=NULL;
  3300.  
  3301.     if (!section) return search_manpath_all(name);
  3302.     j=0;
  3303.     while (sections[j] && sections[j]!=section[0]) j++;
  3304.     if (!sections[j]) return search_manpath_all(name);
  3305.     strcpy(cmpbuf,name);
  3306.     n=strlen(name);
  3307.     cmpbuf[n++]='.';
  3308.     cmpbuf[n++]=section[0];
  3309.     cmpbuf[n]='\0';
  3310.     for (i=0; manpath[i]; i++) {
  3311.     strcpy(smfbuf, manpath[i]);
  3312.     l=strlen(smfbuf);
  3313.     strcpy(smfbuf+l, "man");
  3314.     l+=3;
  3315.     smfbuf[l]=section[0];
  3316.     smfbuf[l+1]='\0';
  3317.     if ((dr=opendir(smfbuf))) {
  3318.         while ((de=readdir(dr))) {
  3319.         if (!strncmp(de->d_name, cmpbuf, n)) {
  3320.             int stlen;
  3321.             if (h) {
  3322.             h->next=(STRDEF*) malloc(sizeof(STRDEF));
  3323.             h=h->next;
  3324.             } else
  3325.             h=foundpages=(STRDEF*) malloc(sizeof(STRDEF));
  3326.             h->nr=i*256+j;
  3327.             stlen=strlen(de->d_name)+1;
  3328.             h->st=(char*) malloc(stlen*sizeof(char));
  3329.             nr++;
  3330.             strcpy(h->st, de->d_name);
  3331.             h->next=NULL;
  3332.         }
  3333.         }
  3334.         closedir(dr);
  3335.     }
  3336.     }
  3337.     return nr;
  3338. }
  3339.  
  3340. static char smfbuf[1000];
  3341.  
  3342. char *search_manpath(char *name)
  3343. {
  3344.     int i,j,n,l,nr=0;
  3345.     struct stat stbuf;
  3346.  
  3347.     for (i=0; manpath[i]; i++) {
  3348.     strcpy(smfbuf, manpath[i]);
  3349.     strcat(smfbuf, name);
  3350.     if (stat(smfbuf, &stbuf) !=-1) return smfbuf;
  3351.     }
  3352.     return NULL;
  3353. }
  3354.  
  3355. int main(int argc, char **argv)
  3356. {
  3357.     FILE *f;
  3358.     struct stat stbuf;
  3359.     char *t=NULL;
  3360.     int l,i;char *buf;
  3361.     int mopt=0;
  3362.     int notinsection=0;
  3363.     char *h, *fullname;
  3364.     STRDEF *stdf;
  3365.  
  3366.     t = getenv("PATH_INFO");
  3367.     if (!t || !*t) /* not :  cgi/man2html/mani/name.i */ {
  3368.     i=1;
  3369.     while (i<argc) {
  3370.         switch (argv[i][0]) {
  3371.         case '-':
  3372.         if (argv[i][1]=='M') {
  3373.             mopt=1;
  3374.             if (i+1<argc) {
  3375.             char *s1, *s2;
  3376.             i++;
  3377.             l=strlen(argv[i]);
  3378.             manpath[0]=(char*) malloc(l+2);
  3379.             s1=manpath[0];
  3380.             s2=argv[i];
  3381.             while ((*s1=*s2)) {
  3382.                 if (*s2=='%') {
  3383.                 s2++;
  3384.                 *s1=0;
  3385.                 if (isdigit(*s2)) *s1=*s2-'0'; else
  3386.                     *s1=tolower(*s2)-'a'+10;
  3387.                 s2++;*s1=(*s1)*16;
  3388.                 if (isdigit(*s2)) *s1=*s1+*s2-'0'; else
  3389.                     *s1=*s1+tolower(*s2)-'a'+10;
  3390.                 }
  3391.                 s1++;
  3392.                 s2++;
  3393.             }
  3394.             if (s1[-1]!='/') { *s1++='/';*s1='\0'; }
  3395.             }
  3396.         }
  3397.         break;
  3398.         case '1': case '2': case '3': case '4': case '5': case '6':
  3399.         case '7': case '8': case '9': case 'n': case 'l':
  3400.         if (!argv[i][1]) {
  3401.             sectionname=argv[i];
  3402.             break;
  3403.         } else
  3404.             if (!argv[i][2] && isalpha(argv[i][1]) &&
  3405.             !isalpha(argv[i][0]) && islower(argv[i][1])) {
  3406.             sectionname=argv[i];
  3407.             break;
  3408.             }
  3409.         default:
  3410.         if (argv[i][0])
  3411.             t=argv[i];
  3412.         break;
  3413.         }
  3414.         i++;
  3415.     }
  3416.     if (t) {
  3417.         char *s1, *s2;
  3418.         i++;
  3419.         l=strlen(t);
  3420.         s2=t;
  3421.         t=s1=(char*) malloc(l);
  3422.         while ((*s1=*s2)) {
  3423.         if (*s2=='%') {
  3424.             s2++;
  3425.             *s1=0;
  3426.             if (isdigit(*s2)) *s1=*s2-'0'; else
  3427.             *s1=tolower(*s2)-'a'+10;
  3428.             s2++;*s1=(*s1)*16;
  3429.             if (isdigit(*s2)) *s1=*s1+*s2-'0'; else
  3430.             *s1=*s1+tolower(*s2)-'a'+10;
  3431.         }
  3432.         s1++;
  3433.         s2++;
  3434.         }
  3435.     }
  3436.     }
  3437.     if (!t || !*t) usage();
  3438.     i=0;
  3439.     h=t;
  3440.  
  3441.     if (strncmp(CGIBASE, "http:", 5) == 0 && strncmp(CGIBASE, "http://", 7) != 0 
  3442.     && getenv("SERVER_NAME")) {
  3443.         char *ptr = CGIBASE;
  3444.         strcpy(location_base, "http://");
  3445.         strcat(location_base, getenv("SERVER_NAME"));
  3446.         strcat(location_base, ptr + 5);        
  3447.     }
  3448.     else {
  3449.         strcpy(location_base, CGIBASE);
  3450.     }
  3451.  
  3452.     while (*h) i=i+(*h++ == '/');
  3453.     if (i==0) {
  3454.     if (sectionname) {
  3455.         char fname[1000];
  3456.         sprintf(fname, "man%c/%s.%s", sectionname[0],t,sectionname);
  3457.         h=search_manpath(fname);
  3458.         if (h) {
  3459.         printf("Location: %s%s\n\n", location_base, h);
  3460.         exit(0);
  3461.         } else {
  3462.         if (sectionname[1]) fname[strlen(fname)-1]='\0';
  3463.         i=search_manpath_section(t,sectionname);
  3464.         notinsection=(i>0);
  3465.         }
  3466.     }
  3467.     if (!i) i=search_manpath_all(t);
  3468.     if (i==1 || (i>1 && mopt)) {
  3469.         printf("Location: %s%sman%c/%s\n\n",
  3470.            location_base,
  3471.            manpath[foundpages->nr/256],
  3472.            sections[foundpages->nr%256],
  3473.            foundpages->st);
  3474.         exit(0);
  3475.     }
  3476.     printf("Content-type: text/html\n\n"
  3477.            "<HTML><HEAD>\n<TITLE>Index to %s manpages.</TITLE>\n"
  3478.            "</HEAD><BODY>\n<H1>Index to %s manpages%s%c</H1>\n", t, t,
  3479.            (notinsection?" for section ":""),
  3480.            (notinsection?sectionname[0]:' '));
  3481.     if (!i)
  3482.         printf("Sorry, no manpages available for %s.\n", t);
  3483.     else {
  3484.         STRDEF *strd;
  3485.         printf("<UL>\n");
  3486.         strd=foundpages;
  3487.         while (strd) {
  3488.         printf("<LI><A HREF=\"" CGIBASE "%sman%c/%s\">"
  3489.                "%s</A> (%s)\n", manpath[strd->nr/256],
  3490.                sections[strd->nr%256], strd->st, strd->st,
  3491.                manpath[strd->nr/256]);
  3492.         strd=strd->next;
  3493.         }
  3494.         printf("</UL>\n");
  3495.     }
  3496.     printf("</BODY></HTML>\n");
  3497.     exit(0);
  3498.     }
  3499.     printf("Content-type: text/html\n\n");
  3500.     h=strstr(t, "man");
  3501.     if (!h) {
  3502.     printf("<HTML><HEAD><TITLE>Manpage: Error</TITLE>\n"
  3503.            "</HEAD><BODY>\n<H1>Only manpages are allowed</H1>\n"
  3504.            "You specified a file which did not contain the keyword\n"
  3505.            "<B>man</B>. To view the file you wanted, use this\n"
  3506.            "<A HREF=\"file:%s\">link</A> instead\n"
  3507.            "</BODY></HTML>\n", t);
  3508.     exit(0);
  3509.     }
  3510.     h=strstr(t,"/../");
  3511.     if (h) {
  3512.     printf("<HTML><HEAD><TITLE>Manpage: Error</TITLE>\n"
  3513.            "</HEAD><BODY>\n<H1>Warning.</H1>\n"
  3514.            "You still try to get files which are manpages. Using the\n"
  3515.            "<B>..</B> construction to get to a different directory will\n"
  3516.            "<B>not</B> work either. If you try this very often, you\n"
  3517.            "will end up in a black list.\n"
  3518.            "</BODY></HTML>\n");
  3519.     exit(0);
  3520.     }
  3521.     h=search_manpath(t);
  3522.     if (!h && i>2) {
  3523.     char *g;
  3524.     g=strrchr(t,'/');
  3525.     *g='.';
  3526.     h=strrchr(t,'/');
  3527.     *g='/';
  3528.     h=search_manpath(h);
  3529.     }
  3530.     if (!h) {
  3531.     h=strrchr(t,'.');
  3532.     if (h) {
  3533.         *h='\0';
  3534.         sectionname=h+1;
  3535.     } else sectionname=NULL;
  3536.     h=strrchr(t,'/');
  3537.     if (!h) h=t; else h++;
  3538.     printf("<HTML><HEAD><TITLE>No manpage for %s.</TITLE>\n"
  3539.            "</HEAD><BODY>\n<H1>No manpage for %s.</H1>\n"
  3540.            "Sorry, the manpage for %s does not exist%s%s",
  3541.            h,h,h, (sectionname? " in section ":""),
  3542.            (sectionname?sectionname:""));
  3543.            i=search_manpath_all(h);
  3544.     if (!i) {
  3545.         if (sectionname)
  3546.         printf(",nor in any other section.\n");
  3547.         else
  3548.         printf("in any section.\n");
  3549.         printf("<HR>\n"
  3550.            "The links to other manual pages are not always correct.\n"
  3551.            "Normally you will get a list of possible replacements,\n"
  3552.            "but in this case the manual page just can't be found.\n");
  3553.     } else {
  3554.         STRDEF *strd;
  3555.         printf(".\nMaybe you can use %s instead.\n<UL>\n",
  3556.            (i>1?"one of the following pages":"this page"));
  3557.         strd=foundpages;
  3558.         while (strd) {
  3559.         printf("<LI><A HREF=\"" CGIBASE "%sman%c/%s\">"
  3560.                "%s</A> (%s)\n", manpath[strd->nr/256],
  3561.                sections[strd->nr%256], strd->st, strd->st,
  3562.                manpath[strd->nr/256]);
  3563.         strd=strd->next;
  3564.         }
  3565.         printf("</UL>\n");
  3566.     }
  3567.     printf("</BODY></HTML>\n");
  3568.     exit(0);
  3569.     }
  3570.     fullname=h;
  3571.     t=strrchr(h,'/');
  3572.     if (t) {
  3573.     *t='\0';
  3574.     if (!chdir(h))
  3575.         h=t+1;
  3576.     *t='/';
  3577.     }
  3578.     if (stat(h, &stbuf)!=-1) l=stbuf.st_size;
  3579.     buf = (char*) malloc((l+5)*sizeof(char));
  3580.     f=fopen(h,"r");
  3581.     if (!f || !buf || !l) {
  3582.     t=strrchr(h,'.');
  3583.     if (t) *t='\0';
  3584.     t=strrchr(h,'/');
  3585.     if (!t) t=h; else t++;
  3586.     printf("<HTML><HEAD><TITLE>No manpage for %s.</TITLE>\n"
  3587.            "</HEAD><BODY>\n<H1>No manpage for %s.</H1>\n"
  3588.            "Sorry, unable to convert the manpage for %s.\n"
  3589.            "</BODY></HTML>\n", t,t,t);
  3590.     exit(0);
  3591.     }
  3592.     i=fread(buf+1,1,l,f);
  3593.     fclose(f);
  3594.     fname=h;
  3595. #ifdef MAKEINDEX
  3596.     idxfile=fopen(INDEXFILE, "a");
  3597. #endif
  3598.     stdf=&standardchar[0];
  3599.     i=0;
  3600.     while (stdf->nr) {
  3601.     stdf->next=&standardchar[i];
  3602.     stdf=stdf->next;
  3603.     i++;
  3604.     }
  3605.     chardef=&standardchar[0];
  3606.     stdf=&standardstring[0];
  3607.     i=0;
  3608.     while (stdf->nr) {
  3609.     stdf->next=&standardstring[i];
  3610.     stdf=stdf->next;
  3611.     i++;
  3612.     }
  3613.     strdef=&standardstring[0];
  3614.     intdef=&standardint[0];
  3615.     i=0;
  3616.     while (intdef->nr) {
  3617.     intdef->next=&standardint[i];
  3618.     intdef=intdef->next;
  3619.     i++;
  3620.     }
  3621.     intdef=&standardint[0];
  3622.     defdef=NULL;
  3623.     buf[0]='\n';
  3624.     buf[l]='\n';
  3625.     buf[l+1]=buf[l+2]='\0';
  3626.     scan_troff(buf+1,0,NULL);
  3627.     while (itemdepth || dl_set[itemdepth]) {
  3628.     out_html("</DL>\n");
  3629.     if (dl_set[itemdepth]) dl_set[itemdepth]=0;
  3630.     else if (itemdepth > 0) itemdepth--;
  3631.     }
  3632.     out_html(change_to_font(0));
  3633.     out_html(change_to_size(0));
  3634.     if (!fillout) {
  3635.     fillout=1;
  3636.     out_html("</PRE>");
  3637.     }
  3638.     out_html(NEWLINE);
  3639.     if (output_possible) {
  3640.     /*   for mosaic users */
  3641.     printf("<HR>\n<A NAME=\"index\"> </A><H2>Index</H2>\n<DL>\n");
  3642.     manidx[mip]=0;
  3643.     printf(manidx);
  3644.     if (subs) printf("</DL>\n");
  3645.     printf("</DL>\n");
  3646.     print_sig();
  3647.     printf("</BODY>\n</HTML>\n");
  3648.     } else {
  3649.     printf("<HTML><HEAD><TITLE>Invalid Manpage</TITLE></HEAD>\n"
  3650.            "<BODY><H1>Invalid Manpage</H1>\n"
  3651.            "You tried to retrieve an incorrect manpage.\n"
  3652.            "The page does not contain a manpage header and will\n"
  3653.            "not produce any output.\n"
  3654.            "If the page is a formatted manpage, you might want to use\n"
  3655.            "a different man2html (or cat2html) converter.\n"
  3656.            "You can also try to load the\n"
  3657.            "<A HREF=\"file://localhost%s\">plain file</A>\n",
  3658.            fullname);
  3659.     print_sig();
  3660.     printf("</BODY>\n</HTML>\n");
  3661.     }
  3662. #ifdef MAKEINDEX
  3663.     if (idxfile) fclose(idxfile);
  3664. #endif
  3665.     if (buf) free(buf);
  3666. }
  3667.